mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 17:02:49 +03:00
Add inherited roles data redaction support to the Data Connector API
[GDC-1292]: https://hasurahq.atlassian.net/browse/GDC-1292?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10023 GitOrigin-RevId: d6947dd89dc59fce3f68b786192c59e731826c60
This commit is contained in:
parent
8c6f823f7b
commit
68f7d6e9a4
@ -129,7 +129,8 @@ The `GET /capabilities` endpoint is used by `graphql-engine` to discover the cap
|
||||
{
|
||||
"capabilities": {
|
||||
"queries": {
|
||||
"foreach": {}
|
||||
"foreach": {},
|
||||
"redaction": {}
|
||||
},
|
||||
"data_schema": {
|
||||
"supports_primary_keys": true,
|
||||
@ -183,6 +184,8 @@ The `config_schema` property contains an [OpenAPI 3 Schema](https://swagger.io/s
|
||||
#### Query capabilities
|
||||
The agent can declare whether or not it supports ["foreach queries"](#foreach-queries) by including a `foreach` property with an empty object assigned to it. Foreach query support is optional, but is required if the agent is to be used as the target of remote relationships in HGE.
|
||||
|
||||
The agent can also declare whether or not it supports ["data redaction"](#data-redaction) by including a `redaction` property with an empty object assigned to it. Data redaction support is optional, but is required if a user configures HGE with inherited roles with different column selection permissions for the same table in the inherited role's role set.
|
||||
|
||||
#### Data schema capabilities
|
||||
The agent can declare whether or not it supports primary keys or foreign keys by setting the `supports_primary_keys` and `supports_foreign_keys` properties under the `data_schema` object on capabilities. If it does not declare support, it is expected that it will not return any such primary/foreign keys in the schema it exposes on the `/schema` endpoint.
|
||||
|
||||
@ -1564,8 +1567,7 @@ The `order_by` field can either be null, which means no particular ordering is r
|
||||
"target_path": [],
|
||||
"target": {
|
||||
"type": "column",
|
||||
"column": "last_name",
|
||||
"column_type": "string"
|
||||
"column": "last_name"
|
||||
},
|
||||
"order_direction": "asc"
|
||||
},
|
||||
@ -1573,8 +1575,7 @@ The `order_by` field can either be null, which means no particular ordering is r
|
||||
"target_path": [],
|
||||
"target": {
|
||||
"type": "column",
|
||||
"column": "first_name",
|
||||
"column_type": "string"
|
||||
"column": "first_name"
|
||||
},
|
||||
"order_direction": "desc"
|
||||
}
|
||||
@ -1824,6 +1825,369 @@ It is important to point out that a `LATERAL` join is necessary instead of regul
|
||||
|
||||
The artificial `Index` column is inserted into the foreach rowset to ensure that the ordering of the results matches the original ordering of the foreach array in the query request.
|
||||
|
||||
#### Data Redaction
|
||||
In HGE, it is possible to create inherited roles; these produce the union of the access rights of the roles in the inherited role's role set. Roles in HGE can grant or deny access to columns on tables and can filter the rows accessible on the table. Naturally, different roles composed together into an inherited role may combine different filters and column access rights for the same table.
|
||||
|
||||
Consider the following `Test` table:
|
||||
|
||||
| Id | ColumnA | ColumnB | ColumnC |
|
||||
|-------|---------|---------|---------|
|
||||
| **1** | **A1** | **B1** | C1 |
|
||||
| **2** | **A2** | **B2** | **C2** |
|
||||
| **3** | A3 | **B3** | **C3** |
|
||||
|
||||
and the following roles defined for this `Test` table:
|
||||
* `RoleA`:
|
||||
* Column Select Permissions: `Id`, `ColumnA`, `ColumnB`
|
||||
* Row Filter: `{ Id: { _in: [1,2] } }`
|
||||
* `RoleB`:
|
||||
* Column Select Permissions: `Id`, `ColumnB`, `ColumnC`
|
||||
* Row Filter: `{ Id: { _in: [2,3] } }`
|
||||
* `ComboRole`: An inherited role, composed of `RoleA` and `RoleB`
|
||||
|
||||
In this scenario, `ComboRole` grants access to all the data that has been **bolded** in the above table. Note that the `A3` is inaccessible because `RoleA` does not grant access to that row, `RoleB` does, but `RoleB` does not grant access to `ColumnA`. Similarly, `C1` is inaccessible because `RoleA` grants access to the row, but not to `ColumnC`, and `RoleB` does not grant access to the row.
|
||||
|
||||
If a user using the `ComboRole` role was to query this `Test` table, we want the access rights as defined above to be enforced, so we'd expect the following data to be returned:
|
||||
|
||||
| Id | ColumnA | ColumnB | ColumnC |
|
||||
|----|---------|---------|---------|
|
||||
| 1 | A1 | B1 | null |
|
||||
| 2 | A2 | B2 | C2 |
|
||||
| 3 | null | B3 | C3 |
|
||||
|
||||
Data redaction is the process by which data is "nulled out" (ie. redacted) by the agent when it should be inaccessible to a user, as it has been above. It is only necessary when different roles in one inherited roles have differing column select permissions. When all the roles have the same column select permissions, the user is prevented from querying inaccessible columns in the first place. Redaction is only necessary when they have partial access to the column, such as in the above example scenario.
|
||||
|
||||
To support data redaction, the agent must declare the `redaction` capability under the `queries` capability.
|
||||
|
||||
```json
|
||||
{
|
||||
"queries": {
|
||||
"redaction": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once this is declared, all query requests that require redaction will contain a `redaction_expressions` property that will contain named redaction expressions per table/function. Then, in every place in the query that requires redaction, a `redaction_expression` property will be defined that refers to the particular named expression that needs to be used.
|
||||
|
||||
A redaction expression is an `Expression`, same as what is used for the `where` query property used in [filters](#filters). If specified, the expression needs to be evaluated per row, and if it evaluates to false, the associated column value must be replaced with null.
|
||||
|
||||
##### Redaction in Fields
|
||||
The first, most obvious, place redaction is applied is against columns requested in a query's `fields`.
|
||||
|
||||
Here's an example query, querying all the columns of the above example `Test` table using the `ComboRole`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"table": ["Test"],
|
||||
"table_relationships": [],
|
||||
// Redaction expressions are defined per table/function
|
||||
"redaction_expressions": [
|
||||
{
|
||||
"target": {
|
||||
"type": "table",
|
||||
"table": ["Test"] // These expressions are defined for the Test table
|
||||
},
|
||||
"expressions": {
|
||||
// Redaction expressions are named (names are only unique within a table/function)
|
||||
"RedactionExp0": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [1,2],
|
||||
"value_type": "number"
|
||||
},
|
||||
"RedactionExp1": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [2,3],
|
||||
"value_type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"query": {
|
||||
"fields": {
|
||||
"Id": {
|
||||
"type": "column",
|
||||
"column": "Id",
|
||||
"column_type": "number"
|
||||
},
|
||||
"ColumnA": {
|
||||
"type": "column",
|
||||
"column": "ColumnA",
|
||||
"column_type": "string",
|
||||
// This column must be redacted using the Test table's RedactionExp0 expression
|
||||
// If this expression evaluates to false for a row, this field must return null for that row
|
||||
"redaction_expression": "RedactionExp0"
|
||||
},
|
||||
"ColumnB": {
|
||||
"type": "column",
|
||||
"column": "ColumnB",
|
||||
"column_type": "string"
|
||||
},
|
||||
"ColumnC": {
|
||||
"type": "column",
|
||||
"column": "ColumnC",
|
||||
"column_type": "string",
|
||||
// This column must be redacted using the Test table's RedactionExp1 expression
|
||||
// If this expression evaluates to false for a row, this field must return null for that row
|
||||
"redaction_expression": "RedactionExp1"
|
||||
}
|
||||
},
|
||||
// The row filters from ComboRole are pushed down into the where filter predicate
|
||||
"where": {
|
||||
"type": "or",
|
||||
"expressions": [
|
||||
{
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [1,2],
|
||||
"value_type": "number"
|
||||
},
|
||||
{
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [2,3],
|
||||
"value_type": "number"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This query might be translated into SQL that is similar to this:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
"Id",
|
||||
CASE
|
||||
WHEN "Id" IN (1,2) THEN "ColumnA"
|
||||
ELSE NULL
|
||||
END AS "ColumnA",
|
||||
"ColumnB",
|
||||
CASE
|
||||
WHEN "Id" IN (2,3) THEN "ColumnC"
|
||||
ELSE NULL
|
||||
END AS "ColumnC"
|
||||
FROM "Test"
|
||||
WHERE "Id" IN (1,2) OR "Id" IN (2,3)
|
||||
```
|
||||
|
||||
##### Redaction in Aggregates
|
||||
Another place redaction can be applied is in aggregates. When aggregations are calculated across a set of rows, the columns being aggregated may need to be redacted _before_ being aggregated over.
|
||||
|
||||
For example, here's an aggregation query:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"table": ["Test"],
|
||||
"table_relationships": [],
|
||||
// Redaction expressions are defined per table/function
|
||||
"redaction_expressions": [
|
||||
{
|
||||
"target": {
|
||||
"type": "table",
|
||||
"table": ["Test"] // These expressions are defined for the Test table
|
||||
},
|
||||
"expressions": {
|
||||
// Redaction expressions are named (names are only unique within a table/function)
|
||||
"RedactionExp0": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [1,2],
|
||||
"value_type": "number"
|
||||
},
|
||||
"RedactionExp1": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [2,3],
|
||||
"value_type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"query": {
|
||||
"aggregates": {
|
||||
// Both "single_column" and "column_count" aggregations can have redaction expressions
|
||||
"aggregate_max_ColumnA": {
|
||||
"type": "single_column",
|
||||
"function": "max",
|
||||
"column": "ColumnA",
|
||||
"redaction_expression": "RedactionExp0",
|
||||
"result_type": "string"
|
||||
},
|
||||
"aggregate_count_ColumnC": {
|
||||
"type": "column_count",
|
||||
"column": "ColumnC",
|
||||
"redaction_expression": "RedactionExp1",
|
||||
"distinct": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This query might be translated into SQL that is similar to this:
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
MAX(
|
||||
CASE
|
||||
WHEN "Id" IN (1,2) THEN "ColumnA"
|
||||
ELSE NULL
|
||||
END
|
||||
) AS "aggregate_max_ColumnA",
|
||||
COUNT(
|
||||
CASE
|
||||
WHEN "Id" IN (2,3) THEN "ColumnC"
|
||||
ELSE NULL
|
||||
END
|
||||
) AS "aggregate_count_ColumnC"
|
||||
FROM "Test"
|
||||
```
|
||||
|
||||
##### Redaction in Filtering
|
||||
When comparing a column to something during filtering, data redaction can require that the column be redacted before the comparison is performed.
|
||||
|
||||
For example, here's a query that uses redaction inside the filter expression in `where`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"table": ["Test"],
|
||||
"table_relationships": [],
|
||||
// Redaction expressions are defined per table/function
|
||||
"redaction_expressions": [
|
||||
{
|
||||
"target": {
|
||||
"type": "table",
|
||||
"table": ["Test"] // These expressions are defined for the Test table
|
||||
},
|
||||
"expressions": {
|
||||
// Redaction expressions are named (names are only unique within a table/function)
|
||||
"RedactionExp0": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [1,2],
|
||||
"value_type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"query": {
|
||||
"fields": {
|
||||
"Id": {
|
||||
"type": "column",
|
||||
"column": "Id",
|
||||
"column_type": "number"
|
||||
}
|
||||
},
|
||||
"where": {
|
||||
"type": "binary_op",
|
||||
"operator": "equals",
|
||||
// ALl column comparisons in "binary_op", "binary_arr_op", and "unary_op"
|
||||
// can potentially have a redaction expression
|
||||
"column": {
|
||||
"name": "ColumnA",
|
||||
"column_type": "string",
|
||||
"redaction_expression": "RedactionExp0"
|
||||
},
|
||||
"value": {
|
||||
"type": "scalar",
|
||||
"value": "A1",
|
||||
"value_type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This query might be translated into SQL that is similar to this:
|
||||
|
||||
```sql
|
||||
SELECT "Id"
|
||||
FROM "Test"
|
||||
WHERE
|
||||
(CASE
|
||||
WHEN "Id" IN (1,2) THEN "ColumnA"
|
||||
ELSE NULL
|
||||
END) = "A1"
|
||||
```
|
||||
|
||||
##### Redaction in Ordering
|
||||
Data redaction also needs to be applied when ordering upon a column; namely the ordering should be performed across the redacted values.
|
||||
|
||||
For example, here's a query that uses redaction inside the `order_by`:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"table": ["Test"],
|
||||
"table_relationships": [],
|
||||
// Redaction expressions are defined per table/function
|
||||
"redaction_expressions": [
|
||||
{
|
||||
"target": {
|
||||
"type": "table",
|
||||
"table": ["Test"] // These expressions are defined for the Test table
|
||||
},
|
||||
"expressions": {
|
||||
// Redaction expressions are named (names are only unique within a table/function)
|
||||
"RedactionExp0": {
|
||||
"type": "binary_arr_op",
|
||||
"operator": "in",
|
||||
"column": { "name": "Id", "column_type": "number" },
|
||||
"values": [1,2],
|
||||
"value_type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"query": {
|
||||
"fields": {
|
||||
"Id": {
|
||||
"type": "column",
|
||||
"column": "Id",
|
||||
"column_type": "number"
|
||||
}
|
||||
},
|
||||
"order_by": {
|
||||
"relations": {},
|
||||
"elements": [
|
||||
{
|
||||
"target_path": [],
|
||||
// Both "column" and "single_column_aggregate"-typed ordering targets can have
|
||||
// redaction expressions applied to them
|
||||
"target": {
|
||||
"type": "column",
|
||||
"column": "ColumnA",
|
||||
"redaction_expression": "RedactionExp0"
|
||||
},
|
||||
"order_direction": "asc"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This query might be translated into SQL that is similar to this:
|
||||
|
||||
```sql
|
||||
SELECT "Id"
|
||||
FROM "Test"
|
||||
ORDER BY
|
||||
(CASE
|
||||
WHEN "Id" IN (1,2) THEN "ColumnA"
|
||||
ELSE NULL
|
||||
END) ASC
|
||||
```
|
||||
|
||||
#### Type Definitions
|
||||
|
||||
The `QueryRequest` TypeScript type in the [reference implementation](./reference/src/types/index.ts) describes the valid request body payloads which may be passed to the `POST /query` endpoint. The response body structure is captured by the `QueryResponse` type.
|
||||
@ -2458,7 +2822,7 @@ Schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"tables": [
|
||||
"tables": [
|
||||
{
|
||||
"name": [
|
||||
"Artist"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.35.0",
|
||||
"version": "0.36.0",
|
||||
"description": "Hasura GraphQL Engine Data Connector Agent API types",
|
||||
"author": "Hasura (https://github.com/hasura/graphql-engine)",
|
||||
"license": "Apache-2.0",
|
||||
|
@ -568,10 +568,14 @@
|
||||
"type": "object"
|
||||
},
|
||||
"ForeachCapabilities": {},
|
||||
"RedactionCapabilities": {},
|
||||
"QueryCapabilities": {
|
||||
"properties": {
|
||||
"foreach": {
|
||||
"$ref": "#/components/schemas/ForeachCapabilities"
|
||||
},
|
||||
"redaction": {
|
||||
"$ref": "#/components/schemas/RedactionCapabilities"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
@ -1710,28 +1714,56 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"Field": {
|
||||
"TNFunction": {
|
||||
"properties": {
|
||||
"function": {
|
||||
"$ref": "#/components/schemas/FunctionName"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"function"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"function",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TNTable": {
|
||||
"properties": {
|
||||
"table": {
|
||||
"$ref": "#/components/schemas/TableName"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"table"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"table",
|
||||
"type"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TargetName": {
|
||||
"discriminator": {
|
||||
"mapping": {
|
||||
"array": "NestedArrayField",
|
||||
"column": "ColumnField",
|
||||
"object": "NestedObjField",
|
||||
"relationship": "RelationshipField"
|
||||
"function": "TNFunction",
|
||||
"table": "TNTable"
|
||||
},
|
||||
"propertyName": "type"
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NestedArrayField"
|
||||
"$ref": "#/components/schemas/TNFunction"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NestedObjField"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ColumnField"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RelationshipField"
|
||||
"$ref": "#/components/schemas/TNTable"
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1753,6 +1785,9 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"RedactionExpressionName": {
|
||||
"type": "string"
|
||||
},
|
||||
"ComparisonColumn": {
|
||||
"properties": {
|
||||
"column_type": {
|
||||
@ -1780,6 +1815,9 @@
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@ -2126,6 +2164,50 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"TargetRedactionExpressions": {
|
||||
"properties": {
|
||||
"expressions": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/components/schemas/Expression"
|
||||
},
|
||||
"description": "The named redaction expressions associated with the target",
|
||||
"type": "object"
|
||||
},
|
||||
"target": {
|
||||
"$ref": "#/components/schemas/TargetName"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"target",
|
||||
"expressions"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Field": {
|
||||
"discriminator": {
|
||||
"mapping": {
|
||||
"array": "NestedArrayField",
|
||||
"column": "ColumnField",
|
||||
"object": "NestedObjField",
|
||||
"relationship": "RelationshipField"
|
||||
},
|
||||
"propertyName": "type"
|
||||
},
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/NestedArrayField"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/NestedObjField"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/ColumnField"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RelationshipField"
|
||||
}
|
||||
]
|
||||
},
|
||||
"OrderByRelation": {
|
||||
"properties": {
|
||||
"subrelations": {
|
||||
@ -2157,6 +2239,9 @@
|
||||
"function": {
|
||||
"$ref": "#/components/schemas/SingleColumnAggregateFunction"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
},
|
||||
"result_type": {
|
||||
"$ref": "#/components/schemas/ScalarType"
|
||||
},
|
||||
@ -2180,6 +2265,9 @@
|
||||
"column": {
|
||||
"type": "string"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"column"
|
||||
@ -2398,6 +2486,9 @@
|
||||
"column_type": {
|
||||
"$ref": "#/components/schemas/ScalarType"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"column"
|
||||
@ -2444,6 +2535,9 @@
|
||||
"function": {
|
||||
"$ref": "#/components/schemas/SingleColumnAggregateFunction"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
},
|
||||
"result_type": {
|
||||
"$ref": "#/components/schemas/ScalarType"
|
||||
},
|
||||
@ -2472,6 +2566,9 @@
|
||||
"description": "Whether or not only distinct items should be counted",
|
||||
"type": "boolean"
|
||||
},
|
||||
"redaction_expression": {
|
||||
"$ref": "#/components/schemas/RedactionExpressionName"
|
||||
},
|
||||
"type": {
|
||||
"enum": [
|
||||
"column_count"
|
||||
@ -2537,6 +2634,14 @@
|
||||
"query": {
|
||||
"$ref": "#/components/schemas/Query"
|
||||
},
|
||||
"redaction_expressions": {
|
||||
"default": [],
|
||||
"description": "Expressions that can be referenced by the query to redact fields/columns",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TargetRedactionExpressions"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"relationships": {
|
||||
"description": "The relationships between entities involved in the entire query request",
|
||||
"items": {
|
||||
@ -2590,6 +2695,14 @@
|
||||
"query": {
|
||||
"$ref": "#/components/schemas/Query"
|
||||
},
|
||||
"redaction_expressions": {
|
||||
"default": [],
|
||||
"description": "Expressions that can be referenced by the query to redact fields/columns",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TargetRedactionExpressions"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"table": {
|
||||
"$ref": "#/components/schemas/TableName"
|
||||
},
|
||||
@ -2702,6 +2815,14 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"redaction_expressions": {
|
||||
"default": [],
|
||||
"description": "Expressions that can be referenced by the query to redact fields/columns",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/TargetRedactionExpressions"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"table_relationships": {
|
||||
"description": "The relationships between tables involved in the entire mutation request",
|
||||
"items": {
|
||||
|
@ -109,6 +109,8 @@ export type { QueryResponse } from './models/QueryResponse';
|
||||
export type { RawCapabilities } from './models/RawCapabilities';
|
||||
export type { RawRequest } from './models/RawRequest';
|
||||
export type { RawResponse } from './models/RawResponse';
|
||||
export type { RedactionCapabilities } from './models/RedactionCapabilities';
|
||||
export type { RedactionExpressionName } from './models/RedactionExpressionName';
|
||||
export type { RelatedTable } from './models/RelatedTable';
|
||||
export type { Relationship } from './models/Relationship';
|
||||
export type { RelationshipCapabilities } from './models/RelationshipCapabilities';
|
||||
@ -137,6 +139,10 @@ export type { TableName } from './models/TableName';
|
||||
export type { TableRelationships } from './models/TableRelationships';
|
||||
export type { TableRequest } from './models/TableRequest';
|
||||
export type { TableType } from './models/TableType';
|
||||
export type { TargetName } from './models/TargetName';
|
||||
export type { TargetRedactionExpressions } from './models/TargetRedactionExpressions';
|
||||
export type { TNFunction } from './models/TNFunction';
|
||||
export type { TNTable } from './models/TNTable';
|
||||
export type { UnaryComparisonOperator } from './models/UnaryComparisonOperator';
|
||||
export type { UniqueIdentifierGenerationStrategy } from './models/UniqueIdentifierGenerationStrategy';
|
||||
export type { UnrelatedTable } from './models/UnrelatedTable';
|
||||
|
@ -2,6 +2,8 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
|
||||
export type ColumnCountAggregate = {
|
||||
/**
|
||||
* The column to apply the count aggregate function to
|
||||
@ -11,6 +13,7 @@ export type ColumnCountAggregate = {
|
||||
* Whether or not only distinct items should be counted
|
||||
*/
|
||||
distinct: boolean;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
type: 'column_count';
|
||||
};
|
||||
|
||||
|
@ -2,11 +2,13 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
import type { ScalarType } from './ScalarType';
|
||||
|
||||
export type ColumnField = {
|
||||
column: string;
|
||||
column_type: ScalarType;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
type: 'column';
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
import type { ScalarType } from './ScalarType';
|
||||
|
||||
export type ComparisonColumn = {
|
||||
@ -14,5 +15,6 @@ export type ComparisonColumn = {
|
||||
* The path to the table that contains the specified column. Missing or empty array means the current table. ["$"] means the query table. No other values are supported at this time.
|
||||
*/
|
||||
path?: Array<string>;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
};
|
||||
|
||||
|
@ -6,6 +6,7 @@ import type { FunctionName } from './FunctionName';
|
||||
import type { FunctionRequestArgument } from './FunctionRequestArgument';
|
||||
import type { Query } from './Query';
|
||||
import type { Relationships } from './Relationships';
|
||||
import type { TargetRedactionExpressions } from './TargetRedactionExpressions';
|
||||
|
||||
export type FunctionRequest = {
|
||||
function: FunctionName;
|
||||
@ -14,6 +15,10 @@ export type FunctionRequest = {
|
||||
*/
|
||||
function_arguments?: Array<FunctionRequestArgument>;
|
||||
query: Query;
|
||||
/**
|
||||
* Expressions that can be referenced by the query to redact fields/columns
|
||||
*/
|
||||
redaction_expressions?: Array<TargetRedactionExpressions>;
|
||||
/**
|
||||
* The relationships between entities involved in the entire query request
|
||||
*/
|
||||
|
@ -5,6 +5,7 @@
|
||||
import type { MutationOperation } from './MutationOperation';
|
||||
import type { TableInsertSchema } from './TableInsertSchema';
|
||||
import type { TableRelationships } from './TableRelationships';
|
||||
import type { TargetRedactionExpressions } from './TargetRedactionExpressions';
|
||||
|
||||
export type MutationRequest = {
|
||||
/**
|
||||
@ -15,6 +16,10 @@ export type MutationRequest = {
|
||||
* The mutation operations to perform
|
||||
*/
|
||||
operations: Array<MutationOperation>;
|
||||
/**
|
||||
* Expressions that can be referenced by the query to redact fields/columns
|
||||
*/
|
||||
redaction_expressions?: Array<TargetRedactionExpressions>;
|
||||
/**
|
||||
* The relationships between tables involved in the entire mutation request
|
||||
*/
|
||||
|
@ -2,8 +2,11 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
|
||||
export type OrderByColumn = {
|
||||
column: string;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
type: 'column';
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
import type { ScalarType } from './ScalarType';
|
||||
import type { SingleColumnAggregateFunction } from './SingleColumnAggregateFunction';
|
||||
|
||||
@ -11,6 +12,7 @@ export type OrderBySingleColumnAggregate = {
|
||||
*/
|
||||
column: string;
|
||||
function: SingleColumnAggregateFunction;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
result_type: ScalarType;
|
||||
type: 'single_column_aggregate';
|
||||
};
|
||||
|
@ -3,8 +3,10 @@
|
||||
/* eslint-disable */
|
||||
|
||||
import type { ForeachCapabilities } from './ForeachCapabilities';
|
||||
import type { RedactionCapabilities } from './RedactionCapabilities';
|
||||
|
||||
export type QueryCapabilities = {
|
||||
foreach?: ForeachCapabilities;
|
||||
redaction?: RedactionCapabilities;
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type RedactionCapabilities = {
|
||||
};
|
||||
|
@ -0,0 +1,5 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
export type RedactionExpressionName = string;
|
@ -2,6 +2,7 @@
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { RedactionExpressionName } from './RedactionExpressionName';
|
||||
import type { ScalarType } from './ScalarType';
|
||||
import type { SingleColumnAggregateFunction } from './SingleColumnAggregateFunction';
|
||||
|
||||
@ -11,6 +12,7 @@ export type SingleColumnAggregate = {
|
||||
*/
|
||||
column: string;
|
||||
function: SingleColumnAggregateFunction;
|
||||
redaction_expression?: RedactionExpressionName;
|
||||
result_type: ScalarType;
|
||||
type: 'single_column';
|
||||
};
|
||||
|
11
dc-agents/dc-api-types/src/models/TNFunction.ts
Normal file
11
dc-agents/dc-api-types/src/models/TNFunction.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { FunctionName } from './FunctionName';
|
||||
|
||||
export type TNFunction = {
|
||||
function: FunctionName;
|
||||
type: 'function';
|
||||
};
|
||||
|
11
dc-agents/dc-api-types/src/models/TNTable.ts
Normal file
11
dc-agents/dc-api-types/src/models/TNTable.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { TableName } from './TableName';
|
||||
|
||||
export type TNTable = {
|
||||
table: TableName;
|
||||
type: 'table';
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import type { Query } from './Query';
|
||||
import type { Relationships } from './Relationships';
|
||||
import type { ScalarValue } from './ScalarValue';
|
||||
import type { TableName } from './TableName';
|
||||
import type { TargetRedactionExpressions } from './TargetRedactionExpressions';
|
||||
|
||||
export type TableRequest = {
|
||||
/**
|
||||
@ -13,6 +14,10 @@ export type TableRequest = {
|
||||
*/
|
||||
foreach?: Array<Record<string, ScalarValue>> | null;
|
||||
query: Query;
|
||||
/**
|
||||
* Expressions that can be referenced by the query to redact fields/columns
|
||||
*/
|
||||
redaction_expressions?: Array<TargetRedactionExpressions>;
|
||||
table: TableName;
|
||||
/**
|
||||
* The relationships between tables involved in the entire query request
|
||||
|
9
dc-agents/dc-api-types/src/models/TargetName.ts
Normal file
9
dc-agents/dc-api-types/src/models/TargetName.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { TNFunction } from './TNFunction';
|
||||
import type { TNTable } from './TNTable';
|
||||
|
||||
export type TargetName = (TNFunction | TNTable);
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
|
||||
import type { Expression } from './Expression';
|
||||
import type { TargetName } from './TargetName';
|
||||
|
||||
export type TargetRedactionExpressions = {
|
||||
/**
|
||||
* The named redaction expressions associated with the target
|
||||
*/
|
||||
expressions: Record<string, Expression>;
|
||||
target: TargetName;
|
||||
};
|
||||
|
10
dc-agents/package-lock.json
generated
10
dc-agents/package-lock.json
generated
@ -24,7 +24,7 @@
|
||||
},
|
||||
"dc-api-types": {
|
||||
"name": "@hasura/dc-api-types",
|
||||
"version": "0.35.0",
|
||||
"version": "0.36.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
@ -2227,7 +2227,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -2547,7 +2547,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify": "^4.13.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -2868,7 +2868,7 @@
|
||||
"version": "file:reference",
|
||||
"requires": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/xml2js": "^0.4.11",
|
||||
@ -3080,7 +3080,7 @@
|
||||
"version": "file:sqlite",
|
||||
"requires": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
"@types/node": "^16.11.49",
|
||||
"@types/sqlite3": "^3.1.8",
|
||||
|
4
dc-agents/reference/package-lock.json
generated
4
dc-agents/reference/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
@ -52,7 +52,7 @@
|
||||
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.35.0",
|
||||
"version": "0.36.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify": "^4.13.0",
|
||||
"mathjs": "^11.0.0",
|
||||
"pino-pretty": "^8.0.0",
|
||||
|
4
dc-agents/sqlite/package-lock.json
generated
4
dc-agents/sqlite/package-lock.json
generated
@ -10,7 +10,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify": "^4.13.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"nanoid": "^3.3.4",
|
||||
@ -57,7 +57,7 @@
|
||||
"integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
|
||||
},
|
||||
"node_modules/@hasura/dc-api-types": {
|
||||
"version": "0.35.0",
|
||||
"version": "0.36.0",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@tsconfig/node16": "^1.0.3",
|
||||
|
@ -22,7 +22,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cors": "^8.1.0",
|
||||
"@hasura/dc-api-types": "0.35.0",
|
||||
"@hasura/dc-api-types": "0.36.0",
|
||||
"fastify-metrics": "^9.2.1",
|
||||
"fastify": "^4.13.0",
|
||||
"nanoid": "^3.3.4",
|
||||
|
@ -1185,6 +1185,7 @@ test-suite graphql-engine-tests
|
||||
Hasura.Backends.DataConnector.API.V0.ColumnSpec
|
||||
Hasura.Backends.DataConnector.API.V0.ConfigSchemaSpec
|
||||
Hasura.Backends.DataConnector.API.V0.ExpressionSpec
|
||||
Hasura.Backends.DataConnector.API.V0.FunctionSpec
|
||||
Hasura.Backends.DataConnector.API.V0.MutationsSpec
|
||||
Hasura.Backends.DataConnector.API.V0.OrderBySpec
|
||||
Hasura.Backends.DataConnector.API.V0.QuerySpec
|
||||
@ -1192,6 +1193,7 @@ test-suite graphql-engine-tests
|
||||
Hasura.Backends.DataConnector.API.V0.ScalarSpec
|
||||
Hasura.Backends.DataConnector.API.V0.SchemaSpec
|
||||
Hasura.Backends.DataConnector.API.V0.TableSpec
|
||||
Hasura.Backends.DataConnector.API.V0.TargetSpec
|
||||
Hasura.Backends.Postgres.Connection.VersionCheckSpec
|
||||
Hasura.Backends.Postgres.Execute.PrepareSpec
|
||||
Hasura.Backends.Postgres.NativeQueries.NativeQueriesSpec
|
||||
|
@ -150,15 +150,15 @@ tests = describe "Aggregate Query Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number")),
|
||||
("ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
[ ("ArtistIds_Id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number") Nothing),
|
||||
("ArtistNames_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing),
|
||||
( "nodes_Albums",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Albums")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap [("nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))]
|
||||
?~ mkFieldsMap [("nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)]
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -280,7 +280,7 @@ tests = describe "Aggregate Query Tests" $ do
|
||||
& API.qAggregates
|
||||
?~ mkFieldsMap
|
||||
[ ("counts_count", API.StarCount),
|
||||
("counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") True)),
|
||||
("counts_uniqueBillingCountries", API.ColumnCount (API.ColumnCountAggregate (API.ColumnName "BillingCountry") Nothing True)),
|
||||
("ids_minimum_Id", API.SingleColumn (singleColumnAggregateMin (API.ColumnName "InvoiceId") (API.ScalarType "number"))),
|
||||
("ids_max_InvoiceId", API.SingleColumn (singleColumnAggregateMax (API.ColumnName "InvoiceId") (API.ScalarType "number")))
|
||||
]
|
||||
@ -309,7 +309,7 @@ tests = describe "Aggregate Query Tests" $ do
|
||||
)
|
||||
|
||||
singleColumnAggregateMax :: API.ColumnName -> API.ScalarType -> API.SingleColumnAggregate
|
||||
singleColumnAggregateMax = API.SingleColumnAggregate $ API.SingleColumnAggregateFunction [G.name|max|]
|
||||
singleColumnAggregateMax columnName scalarType = API.SingleColumnAggregate (API.SingleColumnAggregateFunction [G.name|max|]) columnName Nothing scalarType
|
||||
|
||||
singleColumnAggregateMin :: API.ColumnName -> API.ScalarType -> API.SingleColumnAggregate
|
||||
singleColumnAggregateMin = API.SingleColumnAggregate $ API.SingleColumnAggregateFunction [G.name|min|]
|
||||
singleColumnAggregateMin columnName scalarType = API.SingleColumnAggregate (API.SingleColumnAggregateFunction [G.name|min|]) columnName Nothing scalarType
|
||||
|
@ -144,8 +144,8 @@ tests = describe "Basic Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
@ -188,8 +188,8 @@ tests = describe "Basic Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string"))
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
@ -243,8 +243,8 @@ tests = describe "Basic Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
[ ("id", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number") Nothing),
|
||||
("name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 3 -- The permissions limit is smaller than the query limit, so it is used
|
||||
@ -294,14 +294,14 @@ tests = describe "Basic Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("CustomerId", API.ColumnField (API.ColumnName "CustomerId") $ API.ScalarType "number")
|
||||
[ ("CustomerId", API.ColumnField (API.ColumnName "CustomerId") (API.ScalarType "number") Nothing)
|
||||
]
|
||||
& API.qWhere
|
||||
?~ API.Exists
|
||||
(API.UnrelatedTable $ mkTableName "Employee")
|
||||
( API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "EmployeeId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "EmployeeId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 1) (API.ScalarType "number"))
|
||||
)
|
||||
)
|
||||
|
@ -93,12 +93,12 @@ tests = describe "Custom scalar parsing tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") (API.ScalarType "MyInt") Nothing),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") (API.ScalarType "MyFloat") Nothing),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") (API.ScalarType "MyString") Nothing),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") (API.ScalarType "MyBoolean") Nothing),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") (API.ScalarType "MyID") Nothing),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") (API.ScalarType "MyAnything") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
@ -151,12 +151,12 @@ tests = describe "Custom scalar parsing tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") $ API.ScalarType "MyInt"),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") $ API.ScalarType "MyFloat"),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") $ API.ScalarType "MyString"),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") $ API.ScalarType "MyBoolean"),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") $ API.ScalarType "MyID"),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") $ API.ScalarType "MyAnything")
|
||||
[ ("MyIntColumn", API.ColumnField (API.ColumnName "MyIntColumn") (API.ScalarType "MyInt") Nothing),
|
||||
("MyFloatColumn", API.ColumnField (API.ColumnName "MyFloatColumn") (API.ScalarType "MyFloat") Nothing),
|
||||
("MyStringColumn", API.ColumnField (API.ColumnName "MyStringColumn") (API.ScalarType "MyString") Nothing),
|
||||
("MyBooleanColumn", API.ColumnField (API.ColumnName "MyBooleanColumn") (API.ScalarType "MyBoolean") Nothing),
|
||||
("MyIDColumn", API.ColumnField (API.ColumnName "MyIDColumn") (API.ScalarType "MyID") Nothing),
|
||||
("MyAnythingColumn", API.ColumnField (API.ColumnName "MyAnythingColumn") (API.ScalarType "MyAnything") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
@ -165,27 +165,27 @@ tests = describe "Custom scalar parsing tests" $ do
|
||||
( Set.fromList
|
||||
[ ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyBooleanColumn") (ScalarType "MyBoolean"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyBooleanColumn") (ScalarType "MyBoolean") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.Bool True) (ScalarType "MyBoolean")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyFloatColumn") (ScalarType "MyFloat"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyFloatColumn") (ScalarType "MyFloat") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.Number 3.14) (ScalarType "MyFloat")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyStringColumn") (ScalarType "MyString"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyStringColumn") (ScalarType "MyString") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.String "foo") (ScalarType "MyString")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyIDColumn") (ScalarType "MyID"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyIDColumn") (ScalarType "MyID") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.String "x") (ScalarType "MyID")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyIntColumn") (ScalarType "MyInt"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyIntColumn") (ScalarType "MyInt") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.Number 42.0) (ScalarType "MyInt")),
|
||||
ApplyBinaryComparisonOperator
|
||||
Equal
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyAnythingColumn") (ScalarType "MyAnything"))
|
||||
(ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "MyAnythingColumn") (ScalarType "MyAnything") Nothing)
|
||||
(ScalarValueComparison $ ScalarValue (J.Object mempty) (ScalarType "MyAnything"))
|
||||
]
|
||||
)
|
||||
|
@ -204,17 +204,17 @@ tests = do
|
||||
$ Set.fromList
|
||||
[ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 90) (API.ScalarType "number")),
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.GreaterThan
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 111) (API.ScalarType "number"))
|
||||
],
|
||||
API._dmoReturningFields =
|
||||
mkFieldsMap
|
||||
[ ("deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
[ ("deletedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("deletedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing),
|
||||
( "deletedRows_Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
@ -222,8 +222,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number") Nothing),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
)
|
||||
)
|
||||
@ -312,17 +312,17 @@ tests = do
|
||||
$ Set.fromList
|
||||
[ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 90) (API.ScalarType "number")),
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 112) (API.ScalarType "number"))
|
||||
],
|
||||
API._dmoReturningFields =
|
||||
mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing),
|
||||
( "Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
@ -330,8 +330,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number") Nothing),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -100,8 +100,8 @@ tests = describe "Error Protocol Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
|
@ -221,12 +221,12 @@ tests = do
|
||||
Just
|
||||
$ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "ArtistId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 2) (API.ScalarType "number")),
|
||||
API._imoReturningFields =
|
||||
mkFieldsMap
|
||||
[ ("insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number")),
|
||||
("insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string")),
|
||||
[ ("insertedRows_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("insertedRows_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing),
|
||||
( "insertedRows_Artist",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
@ -234,8 +234,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") $ API.ScalarType "number"),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")
|
||||
[ ("ArtistId", API.ColumnField (API.ColumnName "ArtistId") (API.ScalarType "number") Nothing),
|
||||
("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
)
|
||||
)
|
||||
|
@ -119,13 +119,13 @@ tests = describe "Order By Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 3
|
||||
& API.qOrderBy
|
||||
?~ API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId")) API.Ascending :| [])
|
||||
?~ API.OrderBy mempty (API.OrderByElement [] (API.OrderByColumn (API.ColumnName "AlbumId") Nothing) API.Ascending :| [])
|
||||
)
|
||||
)
|
||||
|
||||
@ -163,7 +163,7 @@ tests = describe "Order By Tests" $ do
|
||||
(mkTableName "Artist")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))]
|
||||
?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)]
|
||||
& API.qLimit
|
||||
?~ 2
|
||||
& API.qOrderBy
|
||||
@ -182,6 +182,7 @@ tests = describe "Order By Tests" $ do
|
||||
$ API.SingleColumnAggregate
|
||||
(API.SingleColumnAggregateFunction [G.name|max|])
|
||||
(API.ColumnName "AlbumId")
|
||||
Nothing
|
||||
(API.ScalarType "number")
|
||||
)
|
||||
API.Ascending
|
||||
|
@ -198,19 +198,19 @@ tests = describe "Object Relationships Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing),
|
||||
( "Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)])
|
||||
)
|
||||
),
|
||||
( "MediaType",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "MediaType")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)])
|
||||
)
|
||||
)
|
||||
]
|
||||
@ -298,7 +298,7 @@ tests = describe "Object Relationships Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string"),
|
||||
[ ("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing),
|
||||
( "Album",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
@ -310,7 +310,7 @@ tests = describe "Object Relationships Tests" $ do
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Artist")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string"))])
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)])
|
||||
)
|
||||
)
|
||||
]
|
||||
@ -338,8 +338,8 @@ tests = describe "Object Relationships Tests" $ do
|
||||
]
|
||||
)
|
||||
( 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
|
||||
[ API.OrderByElement [API.RelationshipName "Album", API.RelationshipName "Artist"] (API.OrderByColumn (API.ColumnName "Name") Nothing) API.Descending,
|
||||
API.OrderByElement [] (API.OrderByColumn (API.ColumnName "Name") Nothing) API.Ascending
|
||||
]
|
||||
)
|
||||
)
|
||||
@ -409,7 +409,7 @@ tests = describe "Object Relationships Tests" $ do
|
||||
(mkTableName "Employee")
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap [("EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") $ API.ScalarType "number")]
|
||||
?~ mkFieldsMap [("EmployeeId", API.ColumnField (API.ColumnName "EmployeeId") (API.ScalarType "number") Nothing)]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
& API.qWhere
|
||||
@ -417,8 +417,8 @@ tests = describe "Object Relationships Tests" $ do
|
||||
(API.RelatedTable $ API.RelationshipName "SupportRepForCustomers")
|
||||
( API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "Country") $ API.ScalarType "string")
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.mkColumnSelector $ API.ColumnName "Country") $ API.ScalarType "string"))
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "Country") (API.ScalarType "string") Nothing)
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.mkColumnSelector $ API.ColumnName "Country") (API.ScalarType "string") Nothing))
|
||||
)
|
||||
& API.qOrderBy
|
||||
?~ API.OrderBy
|
||||
@ -429,8 +429,8 @@ tests = describe "Object Relationships Tests" $ do
|
||||
$ API.Exists (API.RelatedTable $ API.RelationshipName "SupportRep")
|
||||
$ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "Country") $ API.ScalarType "string")
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.mkColumnSelector $ API.ColumnName "Country") $ API.ScalarType "string"))
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "Country") (API.ScalarType "string") Nothing)
|
||||
(API.AnotherColumnComparison (API.ComparisonColumn API.QueryTable (API.mkColumnSelector $ API.ColumnName "Country") (API.ScalarType "string") Nothing))
|
||||
)
|
||||
mempty
|
||||
)
|
||||
|
@ -270,8 +270,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
)
|
||||
& API._QRTable
|
||||
@ -361,8 +361,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
)
|
||||
& API._QRTable
|
||||
@ -464,8 +464,8 @@ tests = do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("nodes_AlbumId", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("nodes_Title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("nodes_AlbumId", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("nodes_Title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qAggregates
|
||||
?~ mkFieldsMap [("aggregate_count", API.StarCount)]
|
||||
|
@ -19,13 +19,13 @@ mkTableName :: Text -> API.TableName
|
||||
mkTableName name = API.TableName (name :| [])
|
||||
|
||||
mkTableRequest :: API.TableName -> API.Query -> API.QueryRequest
|
||||
mkTableRequest tableName query = API.QRTable $ API.TableRequest tableName mempty query Nothing
|
||||
mkTableRequest tableName query = API.QRTable $ API.TableRequest tableName mempty mempty query Nothing
|
||||
|
||||
emptyQuery :: API.Query
|
||||
emptyQuery = API.Query Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
emptyMutationRequest :: API.MutationRequest
|
||||
emptyMutationRequest = API.MutationRequest mempty mempty []
|
||||
emptyMutationRequest = API.MutationRequest mempty mempty mempty []
|
||||
|
||||
mkRowsQueryResponse :: [[(Text, API.FieldValue)]] -> API.QueryResponse
|
||||
mkRowsQueryResponse rows = API.QueryResponse (Just $ mkFieldsMap <$> rows) Nothing
|
||||
|
@ -139,8 +139,8 @@ tests = describe "Transformed Configuration Tests" $ do
|
||||
( emptyQuery
|
||||
& API.qFields
|
||||
?~ mkFieldsMap
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") $ API.ScalarType "number"),
|
||||
("title", API.ColumnField (API.ColumnName "Title") $ API.ScalarType "string")
|
||||
[ ("id", API.ColumnField (API.ColumnName "AlbumId") (API.ScalarType "number") Nothing),
|
||||
("title", API.ColumnField (API.ColumnName "Title") (API.ScalarType "string") Nothing)
|
||||
]
|
||||
& API.qLimit
|
||||
?~ 1
|
||||
|
@ -239,28 +239,28 @@ tests = do
|
||||
$ Set.fromList
|
||||
[ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 3) (API.ScalarType "number")),
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "GenreId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "GenreId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 1) (API.ScalarType "number"))
|
||||
],
|
||||
API._umoPostUpdateCheck =
|
||||
Just
|
||||
$ API.ApplyBinaryComparisonOperator
|
||||
API.GreaterThan
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "UnitPrice") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "UnitPrice") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 0) (API.ScalarType "number")),
|
||||
API._umoReturningFields =
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number") Nothing),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing),
|
||||
( "updatedRows_Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)])
|
||||
)
|
||||
)
|
||||
]
|
||||
@ -366,17 +366,17 @@ tests = do
|
||||
Just
|
||||
$ API.ApplyBinaryComparisonOperator
|
||||
API.GreaterThan
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "UnitPrice") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "UnitPrice") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 0) (API.ScalarType "number"))
|
||||
let sharedReturning =
|
||||
mkFieldsMap
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number")),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string")),
|
||||
[ ("updatedRows_TrackId", API.ColumnField (API.ColumnName "TrackId") (API.ScalarType "number") Nothing),
|
||||
("updatedRows_Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing),
|
||||
( "updatedRows_Genre",
|
||||
API.RelField
|
||||
( API.RelationshipField
|
||||
(API.RelationshipName "Genre")
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") $ API.ScalarType "string")])
|
||||
(emptyQuery & API.qFields ?~ mkFieldsMap [("Name", API.ColumnField (API.ColumnName "Name") (API.ScalarType "string") Nothing)])
|
||||
)
|
||||
)
|
||||
]
|
||||
@ -429,11 +429,11 @@ tests = do
|
||||
$ Set.fromList
|
||||
[ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 3) (API.ScalarType "number")),
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "TrackId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "TrackId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 3) (API.ScalarType "number"))
|
||||
],
|
||||
API._umoPostUpdateCheck = sharedPostUpdateCheck,
|
||||
@ -469,11 +469,11 @@ tests = do
|
||||
$ Set.fromList
|
||||
[ API.ApplyBinaryComparisonOperator
|
||||
API.Equal
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "AlbumId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 3) (API.ScalarType "number")),
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.GreaterThan
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "TrackId") $ API.ScalarType "number")
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "TrackId") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison $ API.ScalarValue (J.Number 3) (API.ScalarType "number"))
|
||||
],
|
||||
API._umoPostUpdateCheck = sharedPostUpdateCheck,
|
||||
|
@ -100,6 +100,7 @@ library
|
||||
Hasura.Backends.DataConnector.API.V0.Scalar
|
||||
Hasura.Backends.DataConnector.API.V0.Schema
|
||||
Hasura.Backends.DataConnector.API.V0.Table
|
||||
Hasura.Backends.DataConnector.API.V0.Target
|
||||
Hasura.Backends.DataConnector.API.V0.Dataset
|
||||
other-modules:
|
||||
Hasura.Backends.DataConnector.API.V0.Name
|
||||
|
@ -15,6 +15,7 @@ module Hasura.Backends.DataConnector.API.V0
|
||||
module Scalar,
|
||||
module Schema,
|
||||
module Table,
|
||||
module Target,
|
||||
module Dataset,
|
||||
)
|
||||
where
|
||||
@ -36,3 +37,4 @@ import Hasura.Backends.DataConnector.API.V0.Relationships as Relationships
|
||||
import Hasura.Backends.DataConnector.API.V0.Scalar as Scalar
|
||||
import Hasura.Backends.DataConnector.API.V0.Schema as Schema
|
||||
import Hasura.Backends.DataConnector.API.V0.Table as Table
|
||||
import Hasura.Backends.DataConnector.API.V0.Target as Target
|
||||
|
@ -16,6 +16,7 @@ import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.OpenApi (ToSchema)
|
||||
import GHC.Generics (Generic)
|
||||
import Hasura.Backends.DataConnector.API.V0.Column qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Expression qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Name (nameCodec)
|
||||
import Hasura.Backends.DataConnector.API.V0.Scalar qualified as API.V0
|
||||
import Language.GraphQL.Draft.Syntax qualified as GQL
|
||||
@ -24,6 +25,7 @@ import Prelude
|
||||
data SingleColumnAggregate = SingleColumnAggregate
|
||||
{ _scaFunction :: SingleColumnAggregateFunction,
|
||||
_scaColumn :: API.V0.ColumnName,
|
||||
_scaRedactionExpression :: Maybe API.V0.RedactionExpressionName,
|
||||
_scaResultType :: API.V0.ScalarType
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
@ -33,6 +35,7 @@ singleColumnAggregateObjectCodec =
|
||||
SingleColumnAggregate
|
||||
<$> requiredField "function" "The aggregation function" .= _scaFunction
|
||||
<*> requiredField "column" "The column to apply the aggregation function to" .= _scaColumn
|
||||
<*> optionalFieldOrNull "redaction_expression" "If present, the name of the redaction expression to evaluate. If the expression is false, the column value must be nulled out before being aggregated." .= _scaRedactionExpression
|
||||
<*> requiredField "result_type" "The scalar type of the result of the aggregate operation" .= _scaResultType
|
||||
|
||||
newtype SingleColumnAggregateFunction = SingleColumnAggregateFunction {unSingleColumnAggregateFunction :: GQL.Name}
|
||||
@ -47,6 +50,7 @@ instance HasCodec SingleColumnAggregateFunction where
|
||||
|
||||
data ColumnCountAggregate = ColumnCountAggregate
|
||||
{ _ccaColumn :: API.V0.ColumnName,
|
||||
_ccaRedactionExpression :: Maybe API.V0.RedactionExpressionName,
|
||||
_ccaDistinct :: Bool
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
@ -55,6 +59,7 @@ columnCountAggregateObjectCodec :: JSONObjectCodec ColumnCountAggregate
|
||||
columnCountAggregateObjectCodec =
|
||||
ColumnCountAggregate
|
||||
<$> requiredField "column" "The column to apply the count aggregate function to" .= _ccaColumn
|
||||
<*> optionalFieldOrNull "redaction_expression" "If present, the name of the redaction expression to evaluate. If the expression is false, the column value must be nulled out before being aggregated." .= _ccaRedactionExpression
|
||||
<*> requiredField "distinct" "Whether or not only distinct items should be counted" .= _ccaDistinct
|
||||
|
||||
data Aggregate
|
||||
|
@ -32,7 +32,9 @@ module Hasura.Backends.DataConnector.API.V0.Capabilities
|
||||
ColumnNullability (..),
|
||||
QueryCapabilities (..),
|
||||
qcForeach,
|
||||
qcRedaction,
|
||||
ForeachCapabilities (..),
|
||||
RedactionCapabilities (..),
|
||||
MutationCapabilities (..),
|
||||
InsertCapabilities (..),
|
||||
UpdateCapabilities (..),
|
||||
@ -173,7 +175,8 @@ instance HasCodec ColumnNullability where
|
||||
]
|
||||
|
||||
data QueryCapabilities = QueryCapabilities
|
||||
{ _qcForeach :: Maybe ForeachCapabilities
|
||||
{ _qcForeach :: Maybe ForeachCapabilities,
|
||||
_qcRedaction :: Maybe RedactionCapabilities
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
@ -184,6 +187,7 @@ instance HasCodec QueryCapabilities where
|
||||
object "QueryCapabilities" $
|
||||
QueryCapabilities
|
||||
<$> optionalField "foreach" "Whether or not the agent supports foreach queries, which are used to enable remote joins to the agent" .= _qcForeach
|
||||
<*> optionalField "redaction" "Whether or not the agent supports redaction expressions in the query" .= _qcRedaction
|
||||
|
||||
data ForeachCapabilities = ForeachCapabilities {}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
@ -194,6 +198,15 @@ instance HasCodec ForeachCapabilities where
|
||||
codec =
|
||||
object "ForeachCapabilities" $ pure ForeachCapabilities
|
||||
|
||||
data RedactionCapabilities = RedactionCapabilities {}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec RedactionCapabilities
|
||||
|
||||
instance HasCodec RedactionCapabilities where
|
||||
codec =
|
||||
object "RedactionCapabilities" $ pure RedactionCapabilities
|
||||
|
||||
data MutationCapabilities = MutationCapabilities
|
||||
{ _mcInsertCapabilities :: Maybe InsertCapabilities,
|
||||
_mcUpdateCapabilities :: Maybe UpdateCapabilities,
|
||||
|
@ -12,6 +12,9 @@ module Hasura.Backends.DataConnector.API.V0.Expression
|
||||
ColumnSelector (..),
|
||||
mkColumnSelector,
|
||||
ComparisonValue (..),
|
||||
TargetRedactionExpressions (..),
|
||||
RedactionExpressionName (..),
|
||||
RedactionExpression (..),
|
||||
)
|
||||
where
|
||||
|
||||
@ -19,8 +22,9 @@ import Autodocodec.Extended
|
||||
import Autodocodec.OpenAPI ()
|
||||
import Control.DeepSeq (NFData)
|
||||
import Control.Lens ((^.), _1, _2, _3, _4)
|
||||
import Data.Aeson (FromJSON, ToJSON, Value)
|
||||
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey, Value)
|
||||
import Data.Data (Data)
|
||||
import Data.HashMap.Strict (HashMap)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.List.NonEmpty (NonEmpty (..))
|
||||
@ -34,6 +38,7 @@ import Hasura.Backends.DataConnector.API.V0.Column qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Relationships qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Scalar qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Table qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Target qualified as API.V0
|
||||
import Prelude
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@ -250,7 +255,10 @@ data ComparisonColumn = ComparisonColumn
|
||||
-- | The name of the column
|
||||
_ccName :: ColumnSelector,
|
||||
-- | The scalar type of the column
|
||||
_ccColumnType :: API.V0.ScalarType
|
||||
_ccColumnType :: API.V0.ScalarType,
|
||||
-- | If present, the name of the redaction expression to evaluate.
|
||||
-- If the expression is false, the column value must be nulled out before being compared to.
|
||||
_ccRedactionExpression :: Maybe RedactionExpressionName
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec ComparisonColumn
|
||||
@ -263,6 +271,7 @@ instance HasCodec ComparisonColumn where
|
||||
<$> optionalFieldWithOmittedDefault "path" CurrentTable "The path to the table that contains the specified column. Missing or empty array means the current table. [\"$\"] means the query table. No other values are supported at this time." .= _ccPath
|
||||
<*> requiredField "name" "The name of the column" .= _ccName
|
||||
<*> requiredField "column_type" "The scalar type of the column" .= _ccColumnType
|
||||
<*> optionalFieldOrNull "redaction_expression" "If present, the name of the redaction expression to evaluate. If the expression is false, the column value must be nulled out before being compared to." .= _ccRedactionExpression
|
||||
|
||||
-- | Describes what table a column is located on. This may either be the "current" table
|
||||
-- (which would be query table, or the table specified by the closest ancestor 'Exists'
|
||||
@ -332,3 +341,35 @@ instance HasCodec ComparisonValue where
|
||||
[ ("column", ("AnotherColumnComparison", mapToDecoder AnotherColumnComparison columnCodec)),
|
||||
("scalar", ("ScalarValueComparison", mapToDecoder ScalarValueComparison objectCodec))
|
||||
]
|
||||
|
||||
data TargetRedactionExpressions = TargetRedactionExpressions
|
||||
{ _treTarget :: API.V0.TargetName,
|
||||
_treExpressions :: HashMap RedactionExpressionName RedactionExpression
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec TargetRedactionExpressions
|
||||
|
||||
instance HasCodec TargetRedactionExpressions where
|
||||
codec =
|
||||
object "TargetRedactionExpressions" $
|
||||
TargetRedactionExpressions
|
||||
<$> requiredField "target" "The target entity with whom the redaction expressions are to be used with" .= _treTarget
|
||||
<*> requiredField "expressions" "The named redaction expressions associated with the target" .= _treExpressions
|
||||
|
||||
newtype RedactionExpressionName = RedactionExpressionName {unRedactionExpressionName :: Text}
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
deriving newtype (ToJSONKey, FromJSONKey)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec RedactionExpressionName
|
||||
|
||||
instance HasCodec RedactionExpressionName where
|
||||
codec = named "RedactionExpressionName" $ dimapCodec RedactionExpressionName unRedactionExpressionName textCodec
|
||||
|
||||
newtype RedactionExpression = RedactionExpression {unRedactionExpression :: Expression}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec RedactionExpression
|
||||
|
||||
instance HasCodec RedactionExpression where
|
||||
codec = dimapCodec RedactionExpression unRedactionExpression codec
|
||||
|
@ -5,6 +5,7 @@
|
||||
module Hasura.Backends.DataConnector.API.V0.Mutations
|
||||
( MutationRequest (..),
|
||||
mrTableRelationships,
|
||||
mrRedactionExpressions,
|
||||
mrInsertSchema,
|
||||
mrOperations,
|
||||
TableInsertSchema (..),
|
||||
@ -82,6 +83,7 @@ import Prelude
|
||||
-- TODO: Does this need to be enhanced ala. QueryRequest to support FunctionRequests?
|
||||
data MutationRequest = MutationRequest
|
||||
{ _mrTableRelationships :: Set API.V0.TableRelationships,
|
||||
_mrRedactionExpressions :: Set API.V0.TargetRedactionExpressions,
|
||||
_mrInsertSchema :: Set TableInsertSchema,
|
||||
_mrOperations :: [MutationOperation]
|
||||
}
|
||||
@ -95,6 +97,8 @@ instance HasCodec MutationRequest where
|
||||
MutationRequest
|
||||
<$> requiredField "table_relationships" "The relationships between tables involved in the entire mutation request"
|
||||
.= _mrTableRelationships
|
||||
<*> optionalFieldWithOmittedDefault "redaction_expressions" mempty "Expressions that can be referenced by the query to redact fields/columns"
|
||||
.= _mrRedactionExpressions
|
||||
<*> requiredField "insert_schema" "The schema by which to interpret row data specified in any insert operations in this request"
|
||||
.= _mrInsertSchema
|
||||
<*> requiredField "operations" "The mutation operations to perform"
|
||||
|
@ -76,7 +76,7 @@ instance HasCodec OrderByElement where
|
||||
<*> requiredField "order_direction" "The direction of ordering to apply" .= _obeOrderDirection
|
||||
|
||||
data OrderByTarget
|
||||
= OrderByColumn API.V0.ColumnName
|
||||
= OrderByColumn API.V0.ColumnName (Maybe API.V0.RedactionExpressionName)
|
||||
| OrderByStarCountAggregate
|
||||
| OrderBySingleColumnAggregate API.V0.SingleColumnAggregate
|
||||
deriving stock (Eq, Generic, Ord, Show)
|
||||
@ -87,16 +87,19 @@ instance HasCodec OrderByTarget where
|
||||
object "OrderByTarget" $
|
||||
discriminatedUnionCodec "type" enc dec
|
||||
where
|
||||
columnCodec = requiredField' "column"
|
||||
columnCodec =
|
||||
(,)
|
||||
<$> requiredField' "column" .= fst
|
||||
<*> optionalFieldOrNull "redaction_expression" "If present, the name of the redaction expression to evaluate. If the expression is false, the column value must be nulled out before being ordered over." .= snd
|
||||
starAggregateCodec = pureCodec ()
|
||||
singleColumnAggregateCodec = API.V0.singleColumnAggregateObjectCodec
|
||||
enc = \case
|
||||
OrderByColumn c -> ("column", mapToEncoder c columnCodec)
|
||||
OrderByColumn c r -> ("column", mapToEncoder (c, r) columnCodec)
|
||||
OrderByStarCountAggregate -> ("star_count_aggregate", mapToEncoder () starAggregateCodec)
|
||||
OrderBySingleColumnAggregate agg -> ("single_column_aggregate", mapToEncoder agg singleColumnAggregateCodec)
|
||||
dec =
|
||||
HashMap.fromList
|
||||
[ ("column", ("OrderByColumn", mapToDecoder OrderByColumn columnCodec)),
|
||||
[ ("column", ("OrderByColumn", mapToDecoder (uncurry OrderByColumn) columnCodec)),
|
||||
("star_count_aggregate", ("OrderByStarCountAggregate", mapToDecoder (const OrderByStarCountAggregate) starAggregateCodec)),
|
||||
("single_column_aggregate", ("OrderBySingleColumnAggregate", mapToDecoder OrderBySingleColumnAggregate singleColumnAggregateCodec))
|
||||
]
|
||||
|
@ -17,12 +17,14 @@ module Hasura.Backends.DataConnector.API.V0.Query
|
||||
qrForeach,
|
||||
trTable,
|
||||
trRelationships,
|
||||
trRedactionExpressions,
|
||||
trQuery,
|
||||
trForeach,
|
||||
frFunction,
|
||||
frFunctionArguments,
|
||||
frQuery,
|
||||
frRelationships,
|
||||
frRedactionExpressions,
|
||||
FieldName (..),
|
||||
Query (..),
|
||||
qFields,
|
||||
@ -72,6 +74,7 @@ import Data.OpenApi (ToSchema)
|
||||
import Data.Set (Set)
|
||||
import Data.Text (Text)
|
||||
import Data.Text qualified as T
|
||||
import Data.Tuple.Extra (fst3, snd3, thd3, uncurry3)
|
||||
import GHC.Generics (Generic)
|
||||
import GHC.Show (appPrec, appPrec1)
|
||||
import Hasura.Backends.DataConnector.API.V0.Aggregate qualified as API.V0
|
||||
@ -125,13 +128,14 @@ instance HasCodec QueryRequest where
|
||||
("function", ("FunctionRequest", mapToDecoder QRFunction objectCodec))
|
||||
]
|
||||
|
||||
pattern TableQueryRequest :: API.V0.TableName -> Set API.V0.Relationships -> Query -> Maybe (NonEmpty (HashMap API.V0.ColumnName API.V0.ScalarValue)) -> QueryRequest
|
||||
pattern TableQueryRequest table relationships query foreach = QRTable (TableRequest table relationships query foreach)
|
||||
pattern TableQueryRequest :: API.V0.TableName -> Set API.V0.Relationships -> Set API.V0.TargetRedactionExpressions -> Query -> Maybe (NonEmpty (HashMap API.V0.ColumnName API.V0.ScalarValue)) -> QueryRequest
|
||||
pattern TableQueryRequest table relationships redactionExps query foreach = QRTable (TableRequest table relationships redactionExps query foreach)
|
||||
|
||||
-- | A serializable request to retrieve strutured data from tables.
|
||||
data TableRequest = TableRequest
|
||||
{ _trTable :: API.V0.TableName,
|
||||
_trRelationships :: Set API.V0.Relationships,
|
||||
_trRedactionExpressions :: Set API.V0.TargetRedactionExpressions,
|
||||
_trQuery :: Query,
|
||||
_trForeach :: Maybe (NonEmpty (HashMap API.V0.ColumnName API.V0.ScalarValue))
|
||||
}
|
||||
@ -146,19 +150,22 @@ instance HasObjectCodec TableRequest where
|
||||
-- NOTE: This can't be done immediately as it would break compatibility in agents.
|
||||
<*> requiredField "table_relationships" "The relationships between tables involved in the entire query request"
|
||||
.= _trRelationships
|
||||
<*> optionalFieldWithOmittedDefault "redaction_expressions" mempty "Expressions that can be referenced by the query to redact fields/columns"
|
||||
.= _trRedactionExpressions
|
||||
<*> requiredField "query" "The details of the query against the table"
|
||||
.= _trQuery
|
||||
<*> optionalFieldOrNull "foreach" "If present, a list of columns and values for the columns that the query must be repeated for, applying the column values as a filter for each query."
|
||||
.= _trForeach
|
||||
|
||||
pattern FunctionQueryRequest :: API.V0.FunctionName -> [FunctionArgument] -> Set API.V0.Relationships -> Query -> QueryRequest
|
||||
pattern FunctionQueryRequest function args relationships query = QRFunction (FunctionRequest function args relationships query)
|
||||
pattern FunctionQueryRequest :: API.V0.FunctionName -> [FunctionArgument] -> Set API.V0.Relationships -> Set API.V0.TargetRedactionExpressions -> Query -> QueryRequest
|
||||
pattern FunctionQueryRequest function args relationships redactionExps query = QRFunction (FunctionRequest function args relationships redactionExps query)
|
||||
|
||||
-- | A serializable request to compute strutured data from a function.
|
||||
data FunctionRequest = FunctionRequest
|
||||
{ _frFunction :: API.V0.FunctionName,
|
||||
_frFunctionArguments :: [FunctionArgument],
|
||||
_frRelationships :: Set API.V0.Relationships,
|
||||
_frRedactionExpressions :: Set API.V0.TargetRedactionExpressions,
|
||||
_frQuery :: Query
|
||||
}
|
||||
deriving stock (Eq, Ord, Show, Generic)
|
||||
@ -217,6 +224,8 @@ instance HasObjectCodec FunctionRequest where
|
||||
.= _frFunctionArguments
|
||||
<*> requiredField "relationships" "The relationships between entities involved in the entire query request"
|
||||
.= _frRelationships
|
||||
<*> optionalFieldWithOmittedDefault "redaction_expressions" mempty "Expressions that can be referenced by the query to redact fields/columns"
|
||||
.= _frRedactionExpressions
|
||||
<*> requiredField "query" "The details of the query against the table"
|
||||
.= _frQuery
|
||||
|
||||
@ -313,7 +322,7 @@ arrayFieldObjectCodec =
|
||||
-- 3. an "object", which indicates that the field contains a nested object
|
||||
-- 4. an "array", which indicates that the field contains a nested array
|
||||
data Field
|
||||
= ColumnField API.V0.ColumnName API.V0.ScalarType
|
||||
= ColumnField API.V0.ColumnName API.V0.ScalarType (Maybe API.V0.RedactionExpressionName)
|
||||
| RelField RelationshipField
|
||||
| NestedObjField API.V0.ColumnName Query
|
||||
| NestedArrayField ArrayField
|
||||
@ -327,11 +336,12 @@ instance HasCodec Field where
|
||||
discriminatedUnionCodec "type" enc dec
|
||||
where
|
||||
columnCodec =
|
||||
(,)
|
||||
(,,)
|
||||
<$> requiredField' "column"
|
||||
.= fst
|
||||
.= fst3
|
||||
<*> requiredField' "column_type"
|
||||
.= snd
|
||||
.= snd3
|
||||
<*> optionalFieldOrNull "redaction_expression" "If present, the name of the redaction expression to evaluate. If the expression is false, the column value must be nulled out." .= thd3
|
||||
nestedObjCodec =
|
||||
(,)
|
||||
<$> requiredField' "column"
|
||||
@ -339,13 +349,13 @@ instance HasCodec Field where
|
||||
<*> requiredField' "query"
|
||||
.= snd
|
||||
enc = \case
|
||||
ColumnField columnName scalarType -> ("column", mapToEncoder (columnName, scalarType) columnCodec)
|
||||
ColumnField columnName scalarType redactionExpName -> ("column", mapToEncoder (columnName, scalarType, redactionExpName) columnCodec)
|
||||
RelField relField -> ("relationship", mapToEncoder relField relationshipFieldObjectCodec)
|
||||
NestedObjField columnName nestedObjQuery -> ("object", mapToEncoder (columnName, nestedObjQuery) nestedObjCodec)
|
||||
NestedArrayField arrayField -> ("array", mapToEncoder arrayField arrayFieldObjectCodec)
|
||||
dec =
|
||||
HashMap.fromList
|
||||
[ ("column", ("ColumnField", mapToDecoder (uncurry ColumnField) columnCodec)),
|
||||
[ ("column", ("ColumnField", mapToDecoder (uncurry3 ColumnField) columnCodec)),
|
||||
("relationship", ("RelationshipField", mapToDecoder RelField relationshipFieldObjectCodec)),
|
||||
("object", ("NestedObjField", mapToDecoder (uncurry NestedObjField) nestedObjCodec)),
|
||||
("array", ("NestedArrayField", mapToDecoder NestedArrayField arrayFieldObjectCodec))
|
||||
|
@ -0,0 +1,48 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE OverloadedLists #-}
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
|
||||
module Hasura.Backends.DataConnector.API.V0.Target
|
||||
( TargetName (..),
|
||||
)
|
||||
where
|
||||
|
||||
import Autodocodec.Extended
|
||||
import Autodocodec.OpenAPI ()
|
||||
import Control.DeepSeq (NFData)
|
||||
import Control.Lens.TH (makePrisms)
|
||||
import Data.Aeson (FromJSON, ToJSON)
|
||||
import Data.Data (Data)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.OpenApi (ToSchema)
|
||||
import GHC.Generics (Generic)
|
||||
import Hasura.Backends.DataConnector.API.V0.Function qualified as API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.Table qualified as API.V0
|
||||
import Prelude
|
||||
|
||||
data TargetName
|
||||
= TNTable API.V0.TableName
|
||||
| TNFunction API.V0.FunctionName
|
||||
deriving stock (Eq, Ord, Show, Generic, Data)
|
||||
deriving anyclass (NFData, Hashable)
|
||||
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec TargetName
|
||||
|
||||
instance HasCodec TargetName where
|
||||
codec = object "TargetName" $ discriminatedUnionCodec "type" enc dec
|
||||
where
|
||||
tableKey = "table"
|
||||
functionKey = "function"
|
||||
tnTableObjectCodec = requiredField "table" "The name of the table to query"
|
||||
tnFunctionObjectCodec = requiredField "function" "The name of the function"
|
||||
enc = \case
|
||||
(TNTable t) -> (tableKey, mapToEncoder t tnTableObjectCodec)
|
||||
(TNFunction f) -> (functionKey, mapToEncoder f tnFunctionObjectCodec)
|
||||
dec =
|
||||
HashMap.fromList
|
||||
[ (tableKey, ("TNTable", mapToDecoder TNTable tnTableObjectCodec)),
|
||||
(functionKey, ("TNFunction", mapToDecoder TNFunction tnFunctionObjectCodec))
|
||||
]
|
||||
|
||||
$(makePrisms ''TargetName)
|
@ -517,8 +517,8 @@ mkTestData schemaResponse testConfig =
|
||||
_tdColumnInsertSchema = columnInsertSchema schemaResponse testConfig,
|
||||
_tdRowColumnOperatorValue = rowColumnOperatorValue schemaResponse testConfig,
|
||||
_tdFindColumnScalarType = \tableName name -> findColumnScalarType schemaResponse tableName (formatColumnName testConfig $ API.ColumnName name),
|
||||
_tdQueryComparisonColumn = API.ComparisonColumn API.QueryTable . API.mkColumnSelector . formatColumnName testConfig . API.ColumnName,
|
||||
_tdCurrentComparisonColumn = API.ComparisonColumn API.CurrentTable . API.mkColumnSelector . formatColumnName testConfig . API.ColumnName,
|
||||
_tdQueryComparisonColumn = \name scalarType -> API.ComparisonColumn API.QueryTable (API.mkColumnSelector . formatColumnName testConfig $ API.ColumnName name) scalarType Nothing,
|
||||
_tdCurrentComparisonColumn = \name scalarType -> API.ComparisonColumn API.CurrentTable (API.mkColumnSelector . formatColumnName testConfig $ API.ColumnName name) scalarType Nothing,
|
||||
_tdOrderByColumn = \targetPath name -> orderByColumn targetPath (formatColumnName testConfig $ API.ColumnName name)
|
||||
}
|
||||
where
|
||||
@ -561,7 +561,7 @@ mkEdgeCasesTestData testConfig schemaResponse =
|
||||
_ectdColumnField = columnField schemaResponse testConfig,
|
||||
_ectdMkDefaultTableInsertSchema = mkDefaultTableInsertSchema schemaResponse testConfig edgeCasesSchemaTables,
|
||||
_ectdRowColumnOperatorValue = rowColumnOperatorValue schemaResponse testConfig,
|
||||
_ectdCurrentComparisonColumn = API.ComparisonColumn API.CurrentTable . API.mkColumnSelector . formatColumnName testConfig . API.ColumnName
|
||||
_ectdCurrentComparisonColumn = \name scalarType -> API.ComparisonColumn API.CurrentTable (API.mkColumnSelector . formatColumnName testConfig $ API.ColumnName name) scalarType Nothing
|
||||
}
|
||||
where
|
||||
tableExists :: API.TableName -> Bool
|
||||
@ -636,7 +636,7 @@ formatColumnName TestConfig {..} = API.ColumnName . applyNameCasing _tcColumnNam
|
||||
|
||||
columnField :: API.SchemaResponse -> TestConfig -> API.TableName -> Text -> API.Field
|
||||
columnField schemaResponse testConfig tableName columnName =
|
||||
API.ColumnField columnName' scalarType
|
||||
API.ColumnField columnName' scalarType Nothing
|
||||
where
|
||||
columnName' = formatColumnName testConfig $ API.ColumnName columnName
|
||||
scalarType = findColumnScalarType schemaResponse tableName columnName'
|
||||
@ -706,7 +706,7 @@ emptyQuery :: API.Query
|
||||
emptyQuery = API.Query Nothing Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
|
||||
emptyMutationRequest :: API.MutationRequest
|
||||
emptyMutationRequest = API.MutationRequest mempty mempty mempty
|
||||
emptyMutationRequest = API.MutationRequest mempty mempty mempty mempty
|
||||
|
||||
sortBy :: (Ixed m, Ord (IxValue m)) => Index m -> [m] -> [m]
|
||||
sortBy propName = sortOn (^? ix propName)
|
||||
@ -784,7 +784,7 @@ scalarValueComparison value valueType = API.ScalarValueComparison $ API.ScalarVa
|
||||
|
||||
orderByColumn :: [API.RelationshipName] -> API.ColumnName -> API.OrderDirection -> API.OrderByElement
|
||||
orderByColumn targetPath columnName orderDirection =
|
||||
API.OrderByElement targetPath (API.OrderByColumn columnName) orderDirection
|
||||
API.OrderByElement targetPath (API.OrderByColumn columnName Nothing) orderDirection
|
||||
|
||||
insertAutoIncPk :: Text -> Integer -> [HashMap API.FieldName API.FieldValue] -> [HashMap API.FieldName API.FieldValue]
|
||||
insertAutoIncPk pkFieldName startingPkId rows =
|
||||
|
@ -27,6 +27,6 @@ spec TestData {..} = describe "Error Protocol" do
|
||||
(CustomBinaryComparisonOperator "FOOBAR")
|
||||
(_tdCurrentComparisonColumn "ArtistId" artistIdScalarType)
|
||||
(Data.scalarValueComparison (Number 1) $ artistIdScalarType)
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
artistIdScalarType = _tdFindColumnScalarType _tdArtistsTableName "ArtistId"
|
||||
|
@ -28,4 +28,4 @@ spec TestData {..} _ = do
|
||||
artistsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("ArtistId", _tdColumnField _tdArtistsTableName "ArtistId"), ("Name", _tdColumnField _tdArtistsTableName "Name")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
@ -453,7 +453,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Delete Mutati
|
||||
invoiceLinesQueryRequest :: QueryRequest
|
||||
invoiceLinesQueryRequest =
|
||||
let query = Data.emptyQuery & qFields ?~ invoiceLinesFields & qOrderBy ?~ OrderBy mempty (_tdOrderByColumn [] "InvoiceId" Ascending :| [])
|
||||
in TableQueryRequest _tdInvoiceLinesTableName mempty query Nothing
|
||||
in TableQueryRequest _tdInvoiceLinesTableName mempty mempty query Nothing
|
||||
|
||||
invoiceIdScalarType = _tdFindColumnScalarType _tdInvoiceLinesTableName "InvoiceId"
|
||||
invoiceLineIdScalarType = _tdFindColumnScalarType _tdInvoiceLinesTableName "InvoiceLineId"
|
||||
|
@ -717,7 +717,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Insert Mutati
|
||||
Data.emptyQuery
|
||||
& qFields ?~ mkFieldsFromExpectedData _tdArtistsTableName (expectedInsertedArtists artistsStartingId)
|
||||
& qWhere ?~ ApplyBinaryArrayComparisonOperator In (_tdCurrentComparisonColumn "ArtistId" artistIdScalarType) (J.Number . fromInteger <$> artistIds) artistIdScalarType
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
albumsQueryRequest :: [Integer] -> QueryRequest
|
||||
albumsQueryRequest albumIds =
|
||||
@ -725,7 +725,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Insert Mutati
|
||||
Data.emptyQuery
|
||||
& qFields ?~ mkFieldsFromExpectedData _tdAlbumsTableName (expectedInsertedAcdcAlbums albumsStartingId)
|
||||
& qWhere ?~ ApplyBinaryArrayComparisonOperator In (_tdCurrentComparisonColumn "AlbumId" albumIdScalarType) (J.Number . fromInteger <$> albumIds) albumIdScalarType
|
||||
in TableQueryRequest _tdAlbumsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdAlbumsTableName mempty mempty query Nothing
|
||||
|
||||
employeesQueryRequest :: [Integer] -> QueryRequest
|
||||
employeesQueryRequest employeeIds =
|
||||
@ -733,7 +733,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Insert Mutati
|
||||
Data.emptyQuery
|
||||
& qFields ?~ mkFieldsFromExpectedData _tdEmployeesTableName (expectedInsertedEmployees employeesStartingId)
|
||||
& qWhere ?~ ApplyBinaryArrayComparisonOperator In (_tdCurrentComparisonColumn "EmployeeId" albumIdScalarType) (J.Number . fromInteger <$> employeeIds) employeeIdScalarType
|
||||
in TableQueryRequest _tdEmployeesTableName mempty query Nothing
|
||||
in TableQueryRequest _tdEmployeesTableName mempty mempty query Nothing
|
||||
|
||||
artistsInsertSchema :: TableInsertSchema
|
||||
artistsInsertSchema = _tdMkDefaultTableInsertSchema _tdArtistsTableName
|
||||
|
@ -719,7 +719,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Update Mutati
|
||||
artistsQueryRequest :: Expression -> QueryRequest
|
||||
artistsQueryRequest whereExp =
|
||||
let query = Data.emptyQuery & qFields ?~ artistsFields & qWhere ?~ whereExp
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
invoiceLinesFields :: HashMap FieldName Field
|
||||
invoiceLinesFields =
|
||||
@ -734,7 +734,7 @@ spec TestData {..} edgeCasesTestData Capabilities {..} = describe "Update Mutati
|
||||
invoiceLinesQueryRequest :: Expression -> QueryRequest
|
||||
invoiceLinesQueryRequest whereExp =
|
||||
let query = Data.emptyQuery & qFields ?~ invoiceLinesFields & qWhere ?~ whereExp
|
||||
in TableQueryRequest _tdInvoiceLinesTableName mempty query Nothing
|
||||
in TableQueryRequest _tdInvoiceLinesTableName mempty mempty query Nothing
|
||||
|
||||
incOperator :: UpdateColumnOperatorName
|
||||
incOperator = UpdateColumnOperatorName $ [G.name|inc|]
|
||||
|
@ -79,7 +79,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
|
||||
describe "Column Count" $ do
|
||||
it "counts all rows with non-null columns" $ do
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") False)]
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing False)]
|
||||
let queryRequest = invoicesQueryRequest aggregates
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -92,7 +92,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
it "can count all rows with non-null values in a column, after applying pagination and filtering" $ do
|
||||
let aggregatesLimit = 50
|
||||
let where' = ApplyBinaryComparisonOperator GreaterThanOrEqual (_tdCurrentComparisonColumn "InvoiceId" invoiceIdScalarType) (Data.scalarValueComparison (Number 380) invoiceIdScalarType)
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") False)]
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing False)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qWhere ?~ where')
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -109,7 +109,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
Data.responseRows response `rowsShouldBe` []
|
||||
|
||||
it "can count all rows with distinct non-null values in a column" $ do
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") True)]
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing True)]
|
||||
let queryRequest = invoicesQueryRequest aggregates
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -124,7 +124,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
let where' = ApplyBinaryComparisonOperator GreaterThanOrEqual (_tdCurrentComparisonColumn "InvoiceId" invoiceIdScalarType) (Data.scalarValueComparison (Number 380) invoiceIdScalarType)
|
||||
-- It is important to add an explicit order by for this query as different database engines will order implicitly resulting in incorrect results
|
||||
let orderBy = OrderBy mempty $ _tdOrderByColumn [] "InvoiceId" Ascending :| []
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") True)]
|
||||
let aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing True)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery %~ (qAggregatesLimit ?~ aggregatesLimit >>> qWhere ?~ where' >>> qOrderBy ?~ orderBy)
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -143,7 +143,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
|
||||
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 aggregates = Data.mkFieldsMap [("count_cols", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing False)]
|
||||
let queryRequest = invoicesQueryRequest aggregates & qrQuery . qLimit ?~ limit
|
||||
response <- queryGuarded queryRequest
|
||||
|
||||
@ -241,7 +241,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
let aggregates =
|
||||
Data.mkFieldsMap
|
||||
[ ("count", StarCount),
|
||||
("distinctBillingStates", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") True),
|
||||
("distinctBillingStates", ColumnCount $ ColumnCountAggregate (_tdColumnName "BillingState") Nothing True),
|
||||
("maxTotal", singleColumnAggregateMax (_tdColumnName "Total") invoiceTotalScalarType)
|
||||
]
|
||||
let queryRequest = invoicesQueryRequest aggregates
|
||||
@ -597,7 +597,7 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
artistOrderBy = OrderBy mempty $ _tdOrderByColumn [] "ArtistId" Ascending :| []
|
||||
artistQuery = Data.emptyQuery & qFields ?~ artistFields & qOrderBy ?~ artistOrderBy
|
||||
artistsTableRelationships = Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships
|
||||
in QRTable $ TableRequest _tdArtistsTableName (Set.fromList [API.RTable artistsTableRelationships]) artistQuery Nothing
|
||||
in QRTable $ TableRequest _tdArtistsTableName (Set.fromList [API.RTable artistsTableRelationships]) mempty artistQuery Nothing
|
||||
|
||||
-- This query is basically what would be generated by this complex HGE GraphQL query
|
||||
-- @
|
||||
@ -673,35 +673,36 @@ spec TestData {..} relationshipCapabilities = describe "Aggregate Queries" $ do
|
||||
API.RTable $ Data.onlyKeepRelationships [_tdInvoiceLinesRelationshipName, _tdMediaTypeRelationshipName] _tdTracksTableRelationships
|
||||
]
|
||||
)
|
||||
mempty
|
||||
artistQuery
|
||||
Nothing
|
||||
|
||||
artistsQueryRequest :: HashMap FieldName Aggregate -> QueryRequest
|
||||
artistsQueryRequest aggregates =
|
||||
let query = Data.emptyQuery & qAggregates ?~ aggregates
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
invoicesQueryRequest :: HashMap FieldName Aggregate -> QueryRequest
|
||||
invoicesQueryRequest aggregates =
|
||||
let query = Data.emptyQuery & qAggregates ?~ aggregates
|
||||
in TableQueryRequest _tdInvoicesTableName mempty query Nothing
|
||||
in TableQueryRequest _tdInvoicesTableName mempty mempty query Nothing
|
||||
|
||||
albumsQueryRequest :: QueryRequest
|
||||
albumsQueryRequest =
|
||||
TableQueryRequest _tdAlbumsTableName mempty Data.emptyQuery Nothing
|
||||
TableQueryRequest _tdAlbumsTableName mempty mempty Data.emptyQuery Nothing
|
||||
|
||||
aggregate :: (NonEmpty a -> Value) -> [a] -> Value
|
||||
aggregate aggFn values =
|
||||
maybe Null aggFn $ NonEmpty.nonEmpty values
|
||||
|
||||
singleColumnAggregateMax :: ColumnName -> ScalarType -> Aggregate
|
||||
singleColumnAggregateMax columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|max|]) columnName resultType
|
||||
singleColumnAggregateMax columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|max|]) columnName Nothing resultType
|
||||
|
||||
singleColumnAggregateMin :: ColumnName -> ScalarType -> Aggregate
|
||||
singleColumnAggregateMin columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|min|]) columnName resultType
|
||||
singleColumnAggregateMin columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|min|]) columnName Nothing resultType
|
||||
|
||||
singleColumnAggregateSum :: ColumnName -> ScalarType -> Aggregate
|
||||
singleColumnAggregateSum columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|sum|]) columnName resultType
|
||||
singleColumnAggregateSum columnName resultType = SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|sum|]) columnName Nothing resultType
|
||||
|
||||
billingCityScalarType = _tdFindColumnScalarType _tdInvoicesTableName "BillingCity"
|
||||
billingCountryScalarType = _tdFindColumnScalarType _tdInvoicesTableName "BillingCountry"
|
||||
|
@ -74,10 +74,10 @@ spec TestData {..} = describe "Basic Queries" $ do
|
||||
artistsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("ArtistId", _tdColumnField _tdArtistsTableName "ArtistId"), ("Name", _tdColumnField _tdArtistsTableName "Name")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
albumsQueryRequest :: QueryRequest
|
||||
albumsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("AlbumId", _tdColumnField _tdAlbumsTableName "AlbumId"), ("ArtistId", _tdColumnField _tdAlbumsTableName "ArtistId"), ("Title", _tdColumnField _tdAlbumsTableName "Title")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdAlbumsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdAlbumsTableName mempty mempty query Nothing
|
||||
|
@ -42,12 +42,12 @@ spec TestData {..} (ScalarTypesCapabilities scalarTypesCapabilities) = describe
|
||||
let queryRequest =
|
||||
let fields = Data.mkFieldsMap [(unColumnName columnName, _tdColumnField tableName (unColumnName columnName))]
|
||||
query' = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest tableName mempty query' Nothing
|
||||
in TableQueryRequest tableName mempty mempty query' Nothing
|
||||
where' =
|
||||
ApplyBinaryComparisonOperator
|
||||
(CustomBinaryComparisonOperator (unName operatorName))
|
||||
(_tdCurrentComparisonColumn (unColumnName columnName) columnType)
|
||||
(AnotherColumnComparison $ ComparisonColumn CurrentTable (mkColumnSelector argColumnName) argType)
|
||||
(AnotherColumnComparison $ ComparisonColumn CurrentTable (mkColumnSelector argColumnName) argType Nothing)
|
||||
query =
|
||||
queryRequest
|
||||
& qrQuery . qWhere ?~ where'
|
||||
|
@ -313,13 +313,13 @@ spec TestData {..} comparisonCapabilities = describe "Filtering in Queries" $ do
|
||||
artistsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("ArtistId", _tdColumnField _tdArtistsTableName "ArtistId"), ("Name", _tdColumnField _tdArtistsTableName "Name")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
albumsQueryRequest :: QueryRequest
|
||||
albumsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("AlbumId", _tdColumnField _tdAlbumsTableName "AlbumId"), ("ArtistId", _tdColumnField _tdAlbumsTableName "ArtistId"), ("Title", _tdColumnField _tdAlbumsTableName "Title")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdAlbumsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdAlbumsTableName mempty mempty query Nothing
|
||||
|
||||
albumIdScalarType = _tdFindColumnScalarType _tdAlbumsTableName "AlbumId"
|
||||
albumTitleScalarType = _tdFindColumnScalarType _tdAlbumsTableName "Title"
|
||||
|
@ -261,13 +261,13 @@ spec TestData {..} Capabilities {..} = describe "Foreach Queries" $ do
|
||||
albumsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("AlbumId", _tdColumnField _tdAlbumsTableName "AlbumId"), ("ArtistId", _tdColumnField _tdAlbumsTableName "ArtistId"), ("Title", _tdColumnField _tdAlbumsTableName "Title")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdAlbumsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdAlbumsTableName mempty mempty query Nothing
|
||||
|
||||
playlistTracksQueryRequest :: QueryRequest
|
||||
playlistTracksQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("PlaylistId", _tdColumnField _tdPlaylistTracksTableName "PlaylistId"), ("TrackId", _tdColumnField _tdPlaylistTracksTableName "TrackId")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdPlaylistTracksTableName mempty query Nothing
|
||||
in TableQueryRequest _tdPlaylistTracksTableName mempty mempty query Nothing
|
||||
|
||||
mkForeachIds :: TableName -> [(Text, J.Value)] -> HashMap ColumnName ScalarValue
|
||||
mkForeachIds tableName =
|
||||
|
@ -409,13 +409,13 @@ spec TestData {..} Capabilities {..} = describe "Order By in Queries" $ do
|
||||
|
||||
albumsQueryRequest :: QueryRequest
|
||||
albumsQueryRequest =
|
||||
TableQueryRequest _tdAlbumsTableName mempty albumsQuery Nothing
|
||||
TableQueryRequest _tdAlbumsTableName mempty mempty albumsQuery Nothing
|
||||
|
||||
artistsQueryRequest :: QueryRequest
|
||||
artistsQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("ArtistId", _tdColumnField _tdArtistsTableName "ArtistId"), ("Name", _tdColumnField _tdArtistsTableName "Name")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdArtistsTableName mempty query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName mempty mempty query Nothing
|
||||
|
||||
tracksQuery :: Query
|
||||
tracksQuery =
|
||||
@ -424,16 +424,16 @@ spec TestData {..} Capabilities {..} = describe "Order By in Queries" $ do
|
||||
|
||||
tracksQueryRequest :: QueryRequest
|
||||
tracksQueryRequest =
|
||||
TableQueryRequest _tdTracksTableName mempty tracksQuery Nothing
|
||||
TableQueryRequest _tdTracksTableName mempty mempty tracksQuery Nothing
|
||||
|
||||
invoicesQueryRequest :: QueryRequest
|
||||
invoicesQueryRequest =
|
||||
let fields = Data.mkFieldsMap [("InvoiceId", _tdColumnField _tdInvoicesTableName "InvoiceId"), ("BillingState", _tdColumnField _tdInvoicesTableName "BillingState")]
|
||||
query = Data.emptyQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdInvoicesTableName mempty query Nothing
|
||||
in TableQueryRequest _tdInvoicesTableName mempty mempty query Nothing
|
||||
|
||||
orderBySingleColumnAggregateMax :: ColumnName -> ScalarType -> OrderByTarget
|
||||
orderBySingleColumnAggregateMax columnName resultType = OrderBySingleColumnAggregate $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|max|]) columnName resultType
|
||||
orderBySingleColumnAggregateMax columnName resultType = OrderBySingleColumnAggregate $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|max|]) columnName Nothing resultType
|
||||
|
||||
albumTitleScalarType = _tdFindColumnScalarType _tdAlbumsTableName "Title"
|
||||
artistNameScalarType = _tdFindColumnScalarType _tdArtistsTableName "Name"
|
||||
|
@ -247,7 +247,7 @@ spec TestData {..} subqueryComparisonCapabilities = describe "Relationship Queri
|
||||
("Artist", RelField $ RelationshipField _tdArtistRelationshipName artistsSubquery)
|
||||
]
|
||||
query = albumsQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdAlbumsTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships]) query Nothing
|
||||
in TableQueryRequest _tdAlbumsTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdArtistRelationshipName] _tdAlbumsTableRelationships]) mempty query Nothing
|
||||
|
||||
artistsWithAlbumsQuery :: (Query -> Query) -> QueryRequest
|
||||
artistsWithAlbumsQuery modifySubquery =
|
||||
@ -261,7 +261,7 @@ spec TestData {..} subqueryComparisonCapabilities = describe "Relationship Queri
|
||||
("Albums", RelField $ RelationshipField _tdAlbumsRelationshipName albumsSubquery)
|
||||
]
|
||||
query = artistsQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdArtistsTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships]) query Nothing
|
||||
in TableQueryRequest _tdArtistsTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdAlbumsRelationshipName] _tdArtistsTableRelationships]) mempty query Nothing
|
||||
|
||||
employeesWithCustomersQuery :: (Query -> Query) -> QueryRequest
|
||||
employeesWithCustomersQuery modifySubquery =
|
||||
@ -273,7 +273,7 @@ spec TestData {..} subqueryComparisonCapabilities = describe "Relationship Queri
|
||||
[ ("SupportRepForCustomers", RelField $ RelationshipField _tdSupportRepForCustomersRelationshipName customersSubquery)
|
||||
]
|
||||
query = employeesQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdEmployeesTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdSupportRepForCustomersRelationshipName] _tdEmployeesTableRelationships]) query Nothing
|
||||
in TableQueryRequest _tdEmployeesTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdSupportRepForCustomersRelationshipName] _tdEmployeesTableRelationships]) mempty query Nothing
|
||||
|
||||
customersWithSupportRepQuery :: (Query -> Query) -> QueryRequest
|
||||
customersWithSupportRepQuery modifySubquery =
|
||||
@ -284,7 +284,7 @@ spec TestData {..} subqueryComparisonCapabilities = describe "Relationship Queri
|
||||
[ ("SupportRep", RelField $ RelationshipField _tdSupportRepRelationshipName supportRepSubquery)
|
||||
]
|
||||
query = customersQuery & qFields ?~ fields
|
||||
in TableQueryRequest _tdCustomersTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdSupportRepRelationshipName] _tdCustomersTableRelationships]) query Nothing
|
||||
in TableQueryRequest _tdCustomersTableName (Set.fromList [API.RTable $ Data.onlyKeepRelationships [_tdSupportRepRelationshipName] _tdCustomersTableRelationships]) mempty query Nothing
|
||||
|
||||
artistsQuery :: Query
|
||||
artistsQuery =
|
||||
|
@ -60,7 +60,7 @@ spec testConfig API.Capabilities {} = describe "supports functions" $ preloadAge
|
||||
k = "take" :: Text.Text
|
||||
v = API.ScalarValue (Number (fromIntegral fibonacciRows)) (API.ScalarType "number")
|
||||
args = [NamedArgument k (API.ScalarArgumentValue v)]
|
||||
in QRFunction $ FunctionRequest _ftdFibonacciFunctionName args mempty query'
|
||||
in FunctionQueryRequest _ftdFibonacciFunctionName args mempty mempty query'
|
||||
|
||||
testData@FunctionsTestData {..} = mkFunctionsTestData preloadedSchema testConfig
|
||||
query = fibonacciRequest testData
|
||||
@ -96,7 +96,7 @@ spec testConfig API.Capabilities {} = describe "supports functions" $ preloadAge
|
||||
relationships = Set.singleton (API.RFunction authorRelationship)
|
||||
v = API.ScalarValue (String "x") (API.ScalarType "string")
|
||||
args = [NamedArgument "query" (API.ScalarArgumentValue v)]
|
||||
in QRFunction $ FunctionRequest _ftdSearchArticlesFunctionName args relationships query'
|
||||
in FunctionQueryRequest _ftdSearchArticlesFunctionName args relationships mempty query'
|
||||
|
||||
testData = mkFunctionsTestData preloadedSchema testConfig
|
||||
query = articlesRequest testData
|
||||
@ -130,7 +130,7 @@ spec testConfig API.Capabilities {} = describe "supports functions" $ preloadAge
|
||||
whereClause =
|
||||
API.ApplyBinaryComparisonOperator
|
||||
API.LessThan
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "id") (API.ScalarType "number"))
|
||||
(API.ComparisonColumn API.CurrentTable (API.mkColumnSelector $ API.ColumnName "id") (API.ScalarType "number") Nothing)
|
||||
(API.ScalarValueComparison (API.ScalarValue (Number 10) (API.ScalarType "number")))
|
||||
query' = Data.emptyQuery & qFields ?~ fields & qWhere ?~ whereClause & qLimit ?~ 2
|
||||
authorRelationship =
|
||||
@ -144,7 +144,7 @@ spec testConfig API.Capabilities {} = describe "supports functions" $ preloadAge
|
||||
relationships = Set.singleton (API.RFunction authorRelationship)
|
||||
v = API.ScalarValue (String "y") (API.ScalarType "string")
|
||||
args = [NamedArgument "query" (API.ScalarArgumentValue v)]
|
||||
in QRFunction $ FunctionRequest _ftdSearchArticlesFunctionName args relationships query'
|
||||
in FunctionQueryRequest _ftdSearchArticlesFunctionName args relationships mempty query'
|
||||
|
||||
testData = mkFunctionsTestData preloadedSchema testConfig
|
||||
query = articlesRequest testData
|
||||
|
@ -60,7 +60,8 @@ capabilities =
|
||||
API._cQueries =
|
||||
Just
|
||||
API.QueryCapabilities
|
||||
{ _qcForeach = Just API.ForeachCapabilities
|
||||
{ _qcForeach = Just API.ForeachCapabilities,
|
||||
_qcRedaction = Just API.RedactionCapabilities
|
||||
},
|
||||
API._cMutations =
|
||||
Just
|
||||
|
@ -53,7 +53,7 @@ instance BackendExecute 'DataConnector where
|
||||
type ExecutionMonad 'DataConnector = AgentClientT
|
||||
|
||||
mkDBQueryPlan UserInfo {..} sourceName sourceConfig ir _headers _gName = do
|
||||
queryPlan@Plan {..} <- flip runReaderT sourceConfig $ Plan.mkQueryPlan _uiSession ir
|
||||
queryPlan@Plan {..} <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkQueryPlan ir
|
||||
transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession)
|
||||
pure
|
||||
DBStepInfo
|
||||
@ -65,7 +65,7 @@ instance BackendExecute 'DataConnector where
|
||||
}
|
||||
|
||||
mkDBQueryExplain fieldName UserInfo {..} sourceName sourceConfig ir _headers _gName = do
|
||||
queryPlan@Plan {..} <- flip runReaderT sourceConfig $ Plan.mkQueryPlan _uiSession ir
|
||||
queryPlan@Plan {..} <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkQueryPlan ir
|
||||
transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession)
|
||||
pure
|
||||
$ mkAnyBackend @'DataConnector
|
||||
@ -78,7 +78,7 @@ instance BackendExecute 'DataConnector where
|
||||
}
|
||||
|
||||
mkDBMutationPlan _env _manager _logger UserInfo {..} _stringifyNum sourceName sourceConfig mutationDB _headers _gName _maybeSelSetArgs = do
|
||||
mutationPlan@Plan {..} <- flip runReaderT sourceConfig $ Plan.mkMutationPlan _uiSession mutationDB
|
||||
mutationPlan@Plan {..} <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkMutationPlan mutationDB
|
||||
transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession)
|
||||
pure
|
||||
DBStepInfo
|
||||
@ -96,7 +96,7 @@ instance BackendExecute 'DataConnector where
|
||||
throw400 NotSupported "mkLiveQuerySubscriptionPlan: not implemented for the Data Connector backend."
|
||||
|
||||
mkDBRemoteRelationshipPlan UserInfo {..} sourceName sourceConfig joinIds joinIdsSchema argumentIdFieldName (resultFieldName, ir) _ _ _ = do
|
||||
remoteRelationshipPlan@Plan {..} <- flip runReaderT sourceConfig $ Plan.mkRemoteRelationshipPlan _uiSession sourceConfig joinIds joinIdsSchema argumentIdFieldName resultFieldName ir
|
||||
remoteRelationshipPlan@Plan {..} <- flip runReaderT (API._cScalarTypes $ _scCapabilities sourceConfig, _uiSession) $ Plan.mkRemoteRelationshipPlan sourceConfig joinIds joinIdsSchema argumentIdFieldName resultFieldName ir
|
||||
transformedSourceConfig <- transformSourceConfig sourceConfig (Just _uiSession)
|
||||
pure
|
||||
DBStepInfo
|
||||
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE DeriveAnyClass #-}
|
||||
{-# LANGUAGE DerivingStrategies #-}
|
||||
|
||||
-- | This module contains Data Connector request/response planning code and utility
|
||||
@ -11,15 +10,19 @@
|
||||
-- for example 'Hasura.Backends.DataConnector.Plan.QueryPlan.mkQueryPlan`.
|
||||
module Hasura.Backends.DataConnector.Plan.Common
|
||||
( Plan (..),
|
||||
writeOutput,
|
||||
replaceOutput,
|
||||
TableRelationships (..),
|
||||
TableRelationshipsKey (..),
|
||||
recordTableRelationship,
|
||||
recordTableRelationshipFromRelInfo,
|
||||
FieldPrefix,
|
||||
noPrefix,
|
||||
prefixWith,
|
||||
applyPrefix,
|
||||
Cardinality (..),
|
||||
recordTableRelationship,
|
||||
recordTableRelationshipFromRelInfo,
|
||||
RedactionExpressionState (..),
|
||||
recordRedactionExpression,
|
||||
translateRedactionExpressions,
|
||||
prepareLiteral,
|
||||
translateBoolExpToExpression,
|
||||
mkRelationshipName,
|
||||
@ -28,7 +31,6 @@ module Hasura.Backends.DataConnector.Plan.Common
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad.Trans.Writer.CPS qualified as CPS
|
||||
import Data.Aeson qualified as J
|
||||
import Data.Aeson.Encoding qualified as JE
|
||||
import Data.Aeson.Types qualified as J
|
||||
@ -42,6 +44,7 @@ import Data.Set qualified as Set
|
||||
import Data.Text qualified as T
|
||||
import Data.Text.Encoding qualified as TE
|
||||
import Data.Text.Extended (toTxt, (<<>), (<>>))
|
||||
import Data.Tuple (swap)
|
||||
import Hasura.Backends.DataConnector.API qualified as API
|
||||
import Hasura.Backends.DataConnector.Adapter.Backend
|
||||
import Hasura.Backends.DataConnector.Adapter.Types
|
||||
@ -70,18 +73,30 @@ data Plan request response = Plan
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- | Key datatype for TableRelationships to avoid having an Either directly as the key,
|
||||
-- and make extending the types of relationships easier in future.
|
||||
data TableRelationshipsKey
|
||||
= FunctionNameKey API.FunctionName
|
||||
| TableNameKey API.TableName
|
||||
deriving stock (Eq, Show, Generic)
|
||||
deriving anyclass (Hashable)
|
||||
-- | Writes some output to state, like one might do if one was using a Writer monad.
|
||||
-- The output is combined with the existing output using '<>' from 'Semigroup'
|
||||
writeOutput :: (Semigroup output, MonadState state m, Has output state) => output -> m ()
|
||||
writeOutput x = modify $ modifier (<> x)
|
||||
|
||||
-- | Replaces some output in the state with a new version of the output. Also, a value
|
||||
-- can be returned from the replacement function.
|
||||
--
|
||||
-- This is useful if you need to inspect the existing state, make a decision, and update it
|
||||
-- based on that decision. The result of the decision can be returned from the transformation
|
||||
-- as your 'a' value.
|
||||
replaceOutput :: (MonadState state m, Has output state) => (output -> (output, a)) -> m a
|
||||
replaceOutput replace = do
|
||||
output <- gets getter
|
||||
let (newOutput, retval) = replace output
|
||||
modify (modifier (const newOutput))
|
||||
pure retval
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- | A monoidal data structure used to record Table Relationships encountered during request
|
||||
-- translation. Used with 'recordTableRelationship'.
|
||||
newtype TableRelationships = TableRelationships
|
||||
{unTableRelationships :: HashMap TableRelationshipsKey (HashMap API.RelationshipName API.Relationship)}
|
||||
{unTableRelationships :: HashMap API.TargetName (HashMap API.RelationshipName API.Relationship)}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
instance Semigroup TableRelationships where
|
||||
@ -93,26 +108,23 @@ instance Monoid TableRelationships where
|
||||
-- | Records a table relationship encountered during request translation into the output of the current
|
||||
-- 'CPS.WriterT'
|
||||
recordTableRelationship ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
MonadError QErr m
|
||||
( MonadState state m,
|
||||
Has TableRelationships state
|
||||
) =>
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
API.RelationshipName ->
|
||||
API.Relationship ->
|
||||
CPS.WriterT writerOutput m ()
|
||||
m ()
|
||||
recordTableRelationship sourceName relationshipName relationship =
|
||||
let newRelationship = TableRelationships $ HashMap.singleton sourceName (HashMap.singleton relationshipName relationship)
|
||||
in CPS.tell $ modifier (const newRelationship) mempty
|
||||
writeOutput $ TableRelationships $ HashMap.singleton sourceName (HashMap.singleton relationshipName relationship)
|
||||
|
||||
recordTableRelationshipFromRelInfo ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
MonadError QErr m
|
||||
( MonadState state m,
|
||||
Has TableRelationships state
|
||||
) =>
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
RelInfo 'DataConnector ->
|
||||
CPS.WriterT writerOutput m (API.RelationshipName, API.Relationship)
|
||||
m (API.RelationshipName, API.Relationship)
|
||||
recordTableRelationshipFromRelInfo sourceTableName RelInfo {..} = do
|
||||
let relationshipName = mkRelationshipName riName
|
||||
let relationshipType = case riType of
|
||||
@ -135,6 +147,55 @@ recordTableRelationshipFromRelInfo sourceTableName RelInfo {..} = do
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- | Collects encountered redaction expressions on a per table/function basis.
|
||||
-- Expressions are deduplicated and assigned a unique name (within that table/function)
|
||||
-- that is then used to reference the expression inside the query.
|
||||
newtype RedactionExpressionState = RedactionExpressionState
|
||||
{unRedactionExpressionState :: HashMap API.TargetName (HashMap API.RedactionExpression API.RedactionExpressionName)}
|
||||
deriving stock (Eq, Show)
|
||||
|
||||
recordRedactionExpression ::
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
API.TargetName ->
|
||||
AnnRedactionExp 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
m (Maybe API.RedactionExpressionName)
|
||||
recordRedactionExpression target = \case
|
||||
NoRedaction -> pure Nothing
|
||||
RedactIfFalse boolExp -> runMaybeT $ do
|
||||
expression <- MaybeT $ fmap API.RedactionExpression <$> translateBoolExpToExpression target boolExp
|
||||
replaceOutput $ \existingState@(RedactionExpressionState recordedExps) ->
|
||||
let targetRecordedExps = fromMaybe mempty $ HashMap.lookup target recordedExps
|
||||
in case HashMap.lookup expression targetRecordedExps of
|
||||
Just existingName -> (existingState, existingName)
|
||||
Nothing ->
|
||||
-- A unique name is generated by counting up from zero as redaction expressions are added
|
||||
-- by using the size of the HashMap they are placed into
|
||||
let newName = API.RedactionExpressionName $ "RedactionExp" <> tshow (HashMap.size targetRecordedExps)
|
||||
newTargetRecordedExps = HashMap.insert expression newName targetRecordedExps
|
||||
newState = RedactionExpressionState $ HashMap.insert target newTargetRecordedExps recordedExps
|
||||
in (newState, newName)
|
||||
|
||||
translateRedactionExpressions :: RedactionExpressionState -> Set API.TargetRedactionExpressions
|
||||
translateRedactionExpressions (RedactionExpressionState redactionsByTarget) =
|
||||
redactionsByTarget
|
||||
& HashMap.toList
|
||||
<&> ( \(targetKey, redactionExps) ->
|
||||
API.TargetRedactionExpressions
|
||||
{ _treTarget = targetKey,
|
||||
_treExpressions = HashMap.fromList $ swap <$> HashMap.toList redactionExps
|
||||
}
|
||||
)
|
||||
& Set.fromList
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- | Represents a potential prefix that can be applied to a field name, useful for
|
||||
-- namespacing field names that may be otherwise duplicated.
|
||||
newtype FieldPrefix = FieldPrefix (Maybe FieldName)
|
||||
@ -166,15 +227,19 @@ data Cardinality
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
prepareLiteral ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
UnpreparedValue 'DataConnector ->
|
||||
m Literal
|
||||
prepareLiteral sessionVariables = \case
|
||||
prepareLiteral = \case
|
||||
UVLiteral literal -> pure $ literal
|
||||
UVParameter _ e -> pure (ValueLiteral (columnTypeToScalarType $ cvType e) (cvValue e))
|
||||
UVSession -> throw400 NotSupported "prepareLiteral: UVSession"
|
||||
UVSessionVar sessionVarType sessionVar -> do
|
||||
sessionVariables <- asks getter
|
||||
textValue <-
|
||||
getSessionVariableValue sessionVar sessionVariables
|
||||
`onNothing` throw400 NotSupported ("prepareLiteral: session var not found: " <>> sessionVar)
|
||||
@ -237,54 +302,54 @@ toColumnSelector (ColumnStack stack) columnName =
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
translateBoolExpToExpression ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
AnnBoolExp 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (Maybe API.Expression)
|
||||
translateBoolExpToExpression sessionVariables sourceName boolExp = do
|
||||
removeAlwaysTrueExpression <$> translateBoolExp sessionVariables sourceName emptyColumnStack boolExp
|
||||
m (Maybe API.Expression)
|
||||
translateBoolExpToExpression sourceName boolExp = do
|
||||
removeAlwaysTrueExpression <$> translateBoolExp sourceName emptyColumnStack boolExp
|
||||
|
||||
translateBoolExp ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
ColumnStack ->
|
||||
AnnBoolExp 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m API.Expression
|
||||
translateBoolExp sessionVariables sourceName columnStack = \case
|
||||
m API.Expression
|
||||
translateBoolExp sourceName columnStack = \case
|
||||
BoolAnd xs ->
|
||||
mkIfZeroOrMany API.And . mapMaybe removeAlwaysTrueExpression <$> traverse (translateBoolExp' sourceName columnStack) xs
|
||||
mkIfZeroOrMany API.And . mapMaybe removeAlwaysTrueExpression <$> traverse (translateBoolExp sourceName columnStack) xs
|
||||
BoolOr xs ->
|
||||
mkIfZeroOrMany API.Or . mapMaybe removeAlwaysFalseExpression <$> traverse (translateBoolExp' sourceName columnStack) xs
|
||||
mkIfZeroOrMany API.Or . mapMaybe removeAlwaysFalseExpression <$> traverse (translateBoolExp sourceName columnStack) xs
|
||||
BoolNot x ->
|
||||
API.Not <$> (translateBoolExp' sourceName columnStack) x
|
||||
BoolField (AVColumn c _redactionExp opExps) -> do
|
||||
-- TODO(redactionExp): Deal with the redaction expression
|
||||
API.Not <$> (translateBoolExp sourceName columnStack) x
|
||||
BoolField (AVColumn c redactionExp opExps) -> do
|
||||
let columnSelector = toColumnSelector columnStack $ ciColumn c
|
||||
lift $ mkIfZeroOrMany API.And <$> traverse (translateOp sessionVariables columnSelector (Witch.from . columnTypeToScalarType $ ciType c)) opExps
|
||||
redactionExpName <- recordRedactionExpression sourceName redactionExp
|
||||
mkIfZeroOrMany API.And <$> traverse (translateOp columnSelector (Witch.from . columnTypeToScalarType $ ciType c) redactionExpName) opExps
|
||||
BoolField (AVNestedObject NestedObjectInfo {..} nestedExp) ->
|
||||
translateBoolExp' sourceName (pushColumn columnStack _noiColumn) nestedExp
|
||||
translateBoolExp sourceName (pushColumn columnStack _noiColumn) nestedExp
|
||||
BoolField (AVRelationship relationshipInfo (RelationshipFilters {rfTargetTablePermissions, rfFilter})) -> do
|
||||
(relationshipName, API.Relationship {..}) <- recordTableRelationshipFromRelInfo sourceName relationshipInfo
|
||||
-- TODO: How does this function keep track of the root table?
|
||||
API.Exists (API.RelatedTable relationshipName) <$> translateBoolExp' (TableNameKey _rTargetTable) emptyColumnStack (BoolAnd [rfTargetTablePermissions, rfFilter])
|
||||
API.Exists (API.RelatedTable relationshipName) <$> translateBoolExp (API.TNTable _rTargetTable) emptyColumnStack (BoolAnd [rfTargetTablePermissions, rfFilter])
|
||||
BoolExists GExists {..} ->
|
||||
let tableName = Witch.from _geTable
|
||||
in API.Exists (API.UnrelatedTable tableName) <$> translateBoolExp' (TableNameKey tableName) emptyColumnStack _geWhere
|
||||
in API.Exists (API.UnrelatedTable tableName) <$> translateBoolExp (API.TNTable tableName) emptyColumnStack _geWhere
|
||||
where
|
||||
translateBoolExp' = translateBoolExp sessionVariables
|
||||
|
||||
-- Makes an 'API.Expression' like 'API.And' if there is zero or many input expressions otherwise
|
||||
-- just returns the singleton expression. This helps remove redundant 'API.And' etcs from the expression.
|
||||
mkIfZeroOrMany :: (Set API.Expression -> API.Expression) -> [API.Expression] -> API.Expression
|
||||
@ -305,14 +370,18 @@ removeAlwaysFalseExpression = \case
|
||||
other -> Just other
|
||||
|
||||
translateOp ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
API.ColumnSelector ->
|
||||
API.ScalarType ->
|
||||
Maybe API.RedactionExpressionName ->
|
||||
OpExpG 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
m API.Expression
|
||||
translateOp sessionVariables columnName columnType opExp = do
|
||||
preparedOpExp <- traverse (prepareLiteral sessionVariables) $ opExp
|
||||
translateOp columnName columnType redactionExpName opExp = do
|
||||
preparedOpExp <- traverse prepareLiteral $ opExp
|
||||
case preparedOpExp of
|
||||
AEQ _ (ValueLiteral scalarType value) ->
|
||||
pure $ mkApplyBinaryComparisonOperatorToScalar API.Equal value scalarType
|
||||
@ -370,7 +439,7 @@ translateOp sessionVariables columnName columnType opExp = do
|
||||
pure $ API.ApplyBinaryArrayComparisonOperator (API.CustomBinaryArrayComparisonOperator _cboName) currentComparisonColumn array (Witch.from scalarType)
|
||||
where
|
||||
currentComparisonColumn :: API.ComparisonColumn
|
||||
currentComparisonColumn = API.ComparisonColumn API.CurrentTable columnName columnType
|
||||
currentComparisonColumn = API.ComparisonColumn API.CurrentTable columnName columnType redactionExpName
|
||||
|
||||
mkApplyBinaryComparisonOperatorToAnotherColumn :: API.BinaryComparisonOperator -> RootOrCurrentColumn 'DataConnector -> API.Expression
|
||||
mkApplyBinaryComparisonOperatorToAnotherColumn operator (RootOrCurrentColumn rootOrCurrent otherColumnName) =
|
||||
@ -379,7 +448,7 @@ translateOp sessionVariables columnName columnType opExp = do
|
||||
IsCurrent -> API.CurrentTable
|
||||
otherColumnSelector = API.mkColumnSelector $ Witch.from otherColumnName
|
||||
in -- TODO(dmoverton): allow otherColumnName to refer to nested object fields.
|
||||
API.ApplyBinaryComparisonOperator operator currentComparisonColumn (API.AnotherColumnComparison $ API.ComparisonColumn columnPath otherColumnSelector columnType)
|
||||
API.ApplyBinaryComparisonOperator operator currentComparisonColumn (API.AnotherColumnComparison $ API.ComparisonColumn columnPath otherColumnSelector columnType Nothing)
|
||||
|
||||
inOperator :: Literal -> API.Expression
|
||||
inOperator literal =
|
||||
|
@ -3,10 +3,9 @@ module Hasura.Backends.DataConnector.Plan.MutationPlan
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad.Trans.Writer.CPS qualified as CPS
|
||||
import Data.Aeson qualified as J
|
||||
import Data.Aeson.Encoding qualified as JE
|
||||
import Data.Has (Has, modifier)
|
||||
import Data.Has (Has)
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.Semigroup.Foldable (toNonEmpty)
|
||||
import Data.Set (Set)
|
||||
@ -62,36 +61,41 @@ instance Semigroup TableInsertSchema where
|
||||
}
|
||||
|
||||
recordTableInsertSchema ::
|
||||
( Has TableInsertSchemas writerOutput,
|
||||
Monoid writerOutput,
|
||||
MonadError QErr m
|
||||
( Has TableInsertSchemas state,
|
||||
MonadState state m
|
||||
) =>
|
||||
API.TableName ->
|
||||
TableInsertSchema ->
|
||||
CPS.WriterT writerOutput m ()
|
||||
m ()
|
||||
recordTableInsertSchema tableName tableInsertSchema =
|
||||
let newTableSchema = TableInsertSchemas $ HashMap.singleton tableName tableInsertSchema
|
||||
in CPS.tell $ modifier (const newTableSchema) mempty
|
||||
writeOutput . TableInsertSchemas $ HashMap.singleton tableName tableInsertSchema
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
mkMutationPlan ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
MutationDB 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
m (Plan API.MutationRequest API.MutationResponse)
|
||||
mkMutationPlan sessionVariables mutationDB = do
|
||||
request <- translateMutationDB sessionVariables mutationDB
|
||||
mkMutationPlan mutationDB = do
|
||||
request <- translateMutationDB mutationDB
|
||||
pure $ Plan request (reshapeResponseToMutationGqlShape mutationDB)
|
||||
|
||||
translateMutationDB ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
MutationDB 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
m API.MutationRequest
|
||||
translateMutationDB sessionVariables = \case
|
||||
translateMutationDB = \case
|
||||
MDBInsert insert -> do
|
||||
(insertOperation, (tableRelationships, tableInsertSchemas)) <- CPS.runWriterT $ translateInsert sessionVariables insert
|
||||
(insertOperation, (tableRelationships, redactionExpressionState, tableInsertSchemas)) <-
|
||||
flip runStateT (mempty, RedactionExpressionState mempty, mempty) $ translateInsert insert
|
||||
let apiTableInsertSchema =
|
||||
unTableInsertSchemas tableInsertSchemas
|
||||
& HashMap.toList
|
||||
@ -100,11 +104,13 @@ translateMutationDB sessionVariables = \case
|
||||
pure
|
||||
$ API.MutationRequest
|
||||
{ _mrTableRelationships = apiTableRelationships,
|
||||
_mrRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_mrInsertSchema = Set.fromList apiTableInsertSchema,
|
||||
_mrOperations = [API.InsertOperation insertOperation]
|
||||
}
|
||||
MDBUpdate update -> do
|
||||
(updateOperations, tableRelationships) <- CPS.runWriterT $ translateUpdate sessionVariables update
|
||||
(updateOperations, (tableRelationships, redactionExpressionState)) <-
|
||||
flip runStateT (mempty, RedactionExpressionState mempty) $ translateUpdate update
|
||||
let apiTableRelationships =
|
||||
Set.fromList
|
||||
$ uncurry API.TableRelationships
|
||||
@ -112,11 +118,13 @@ translateMutationDB sessionVariables = \case
|
||||
pure
|
||||
$ API.MutationRequest
|
||||
{ _mrTableRelationships = apiTableRelationships,
|
||||
_mrRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_mrInsertSchema = mempty,
|
||||
_mrOperations = API.UpdateOperation <$> updateOperations
|
||||
}
|
||||
MDBDelete delete -> do
|
||||
(deleteOperation, tableRelationships) <- CPS.runWriterT $ translateDelete sessionVariables delete
|
||||
(deleteOperation, (tableRelationships, redactionExpressionState)) <-
|
||||
flip runStateT (mempty, RedactionExpressionState mempty) $ translateDelete delete
|
||||
let apiTableRelationships =
|
||||
Set.fromList
|
||||
$ uncurry API.TableRelationships
|
||||
@ -124,26 +132,34 @@ translateMutationDB sessionVariables = \case
|
||||
pure
|
||||
$ API.MutationRequest
|
||||
{ _mrTableRelationships = apiTableRelationships,
|
||||
_mrRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_mrInsertSchema = mempty,
|
||||
_mrOperations = [API.DeleteOperation deleteOperation]
|
||||
}
|
||||
MDBFunction _returnsSet _select ->
|
||||
throw400 NotSupported "translateMutationDB: function mutations not implemented for the Data Connector backend."
|
||||
|
||||
eitherKey :: (TableRelationshipsKey, c) -> Either (API.FunctionName, c) (API.TableName, c)
|
||||
eitherKey (FunctionNameKey f, x) = Left (f, x)
|
||||
eitherKey (TableNameKey t, x) = Right (t, x)
|
||||
eitherKey :: (API.TargetName, c) -> Either (API.FunctionName, c) (API.TableName, c)
|
||||
eitherKey (API.TNFunction f, x) = Left (f, x)
|
||||
eitherKey (API.TNTable t, x) = Right (t, x)
|
||||
|
||||
translateInsert ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
Has TableInsertSchemas state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnotatedInsert 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT (TableRelationships, TableInsertSchemas) m API.InsertMutationOperation
|
||||
translateInsert sessionVariables AnnotatedInsert {_aiData = AnnotatedInsertData {..}, ..} = do
|
||||
m API.InsertMutationOperation
|
||||
translateInsert AnnotatedInsert {_aiData = AnnotatedInsertData {..}, ..} = do
|
||||
captureTableInsertSchema tableName _aiTableColumns _aiPrimaryKey _aiExtraTableMetadata
|
||||
rows <- lift $ traverse (translateInsertRow sessionVariables tableName _aiTableColumns _aiPresetValues) _aiInsertObject
|
||||
postInsertCheck <- translateBoolExpToExpression sessionVariables (TableNameKey tableName) insertCheckCondition
|
||||
returningFields <- translateMutationOutputToReturningFields sessionVariables tableName _aiOutput
|
||||
rows <- traverse (translateInsertRow tableName _aiTableColumns _aiPresetValues) _aiInsertObject
|
||||
postInsertCheck <- translateBoolExpToExpression (API.TNTable tableName) insertCheckCondition
|
||||
returningFields <- translateMutationOutputToReturningFields tableName _aiOutput
|
||||
pure
|
||||
$ API.InsertMutationOperation
|
||||
{ API._imoTable = tableName,
|
||||
@ -157,15 +173,14 @@ translateInsert sessionVariables AnnotatedInsert {_aiData = AnnotatedInsertData
|
||||
(insertCheckCondition, _updateCheckCondition) = _aiCheckCondition
|
||||
|
||||
captureTableInsertSchema ::
|
||||
( Has TableInsertSchemas writerOutput,
|
||||
Monoid writerOutput,
|
||||
MonadError QErr m
|
||||
( MonadState state m,
|
||||
Has TableInsertSchemas state
|
||||
) =>
|
||||
API.TableName ->
|
||||
[ColumnInfo 'DataConnector] ->
|
||||
Maybe (NESeq ColumnName) ->
|
||||
ExtraTableMetadata ->
|
||||
CPS.WriterT writerOutput m ()
|
||||
m ()
|
||||
captureTableInsertSchema tableName tableColumns primaryKey ExtraTableMetadata {..} = do
|
||||
let fieldSchemas =
|
||||
tableColumns
|
||||
@ -183,20 +198,23 @@ captureTableInsertSchema tableName tableColumns primaryKey ExtraTableMetadata {.
|
||||
recordTableInsertSchema tableName $ TableInsertSchema primaryKey' fieldSchemas
|
||||
|
||||
translateInsertRow ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
API.TableName ->
|
||||
[ColumnInfo 'DataConnector] ->
|
||||
HashMap ColumnName (UnpreparedValue 'DataConnector) ->
|
||||
AnnotatedInsertRow 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
m API.RowObject
|
||||
translateInsertRow sessionVariables tableName tableColumns defaultColumnValues insertRow = do
|
||||
translateInsertRow tableName tableColumns defaultColumnValues insertRow = do
|
||||
columnSchemasAndValues <- forM (HashMap.toList columnUnpreparedValues) $ \(columnName, columnValue) -> do
|
||||
fieldName <-
|
||||
case find (\ColumnInfo {..} -> ciColumn == columnName) tableColumns of
|
||||
Just ColumnInfo {..} -> pure . API.FieldName $ G.unName ciName
|
||||
Nothing -> throw500 $ "Can't find column " <> toTxt columnName <> " in table schema for " <> API.tableNameToText tableName
|
||||
preparedLiteral <- prepareLiteral sessionVariables columnValue
|
||||
preparedLiteral <- prepareLiteral columnValue
|
||||
|
||||
value <-
|
||||
case preparedLiteral of
|
||||
@ -223,26 +241,38 @@ translateInsertRow sessionVariables tableName tableColumns defaultColumnValues i
|
||||
& HashMap.fromList
|
||||
|
||||
translateUpdate ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnotatedUpdateG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT TableRelationships m [API.UpdateMutationOperation]
|
||||
translateUpdate sessionVariables annUpdate@AnnotatedUpdateG {..} = do
|
||||
m [API.UpdateMutationOperation]
|
||||
translateUpdate annUpdate@AnnotatedUpdateG {..} = do
|
||||
case _auUpdateVariant of
|
||||
SingleBatch batch -> (: []) <$> translateUpdateBatch sessionVariables annUpdate batch
|
||||
MultipleBatches batches -> traverse (translateUpdateBatch sessionVariables annUpdate) batches
|
||||
SingleBatch batch -> (: []) <$> translateUpdateBatch annUpdate batch
|
||||
MultipleBatches batches -> traverse (translateUpdateBatch annUpdate) batches
|
||||
|
||||
translateUpdateBatch ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnotatedUpdateG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
UpdateBatch 'DataConnector UpdateOperator (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT TableRelationships m API.UpdateMutationOperation
|
||||
translateUpdateBatch sessionVariables AnnotatedUpdateG {..} UpdateBatch {..} = do
|
||||
updates <- lift $ translateUpdateOperations sessionVariables _ubOperations
|
||||
whereExp <- translateBoolExpToExpression sessionVariables (TableNameKey tableName) (BoolAnd [_auUpdatePermissions, _ubWhere])
|
||||
postUpdateCheck <- translateBoolExpToExpression sessionVariables (TableNameKey tableName) _auCheck
|
||||
returningFields <- translateMutationOutputToReturningFields sessionVariables tableName _auOutput
|
||||
m API.UpdateMutationOperation
|
||||
translateUpdateBatch AnnotatedUpdateG {..} UpdateBatch {..} = do
|
||||
updates <- translateUpdateOperations _ubOperations
|
||||
whereExp <- translateBoolExpToExpression (API.TNTable tableName) (BoolAnd [_auUpdatePermissions, _ubWhere])
|
||||
postUpdateCheck <- translateBoolExpToExpression (API.TNTable tableName) _auCheck
|
||||
returningFields <- translateMutationOutputToReturningFields tableName _auOutput
|
||||
|
||||
pure
|
||||
$ API.UpdateMutationOperation
|
||||
@ -257,11 +287,14 @@ translateUpdateBatch sessionVariables AnnotatedUpdateG {..} UpdateBatch {..} = d
|
||||
|
||||
translateUpdateOperations ::
|
||||
forall m r.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
HashMap ColumnName (UpdateOperator (UnpreparedValue 'DataConnector)) ->
|
||||
m (Set API.RowUpdate)
|
||||
translateUpdateOperations sessionVariables columnUpdates =
|
||||
translateUpdateOperations columnUpdates =
|
||||
fmap Set.fromList . forM (HashMap.toList columnUpdates) $ \(columnName, updateOperator) -> do
|
||||
let (mkRowUpdate, value) =
|
||||
case updateOperator of
|
||||
@ -273,19 +306,25 @@ translateUpdateOperations sessionVariables columnUpdates =
|
||||
where
|
||||
prepareAndExtractLiteralValue :: UnpreparedValue 'DataConnector -> m (ScalarType, J.Value)
|
||||
prepareAndExtractLiteralValue unpreparedValue = do
|
||||
preparedLiteral <- prepareLiteral sessionVariables unpreparedValue
|
||||
preparedLiteral <- prepareLiteral unpreparedValue
|
||||
case preparedLiteral of
|
||||
ValueLiteral scalarType value -> pure (scalarType, value)
|
||||
ArrayLiteral _scalarType _values -> throw400 NotSupported "translateUpdateOperations: Array literals are not supported as column update values"
|
||||
|
||||
translateDelete ::
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnDelG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT TableRelationships m API.DeleteMutationOperation
|
||||
translateDelete sessionVariables AnnDel {..} = do
|
||||
whereExp <- translateBoolExpToExpression sessionVariables (TableNameKey tableName) (BoolAnd [permissionFilter, whereClause])
|
||||
returningFields <- translateMutationOutputToReturningFields sessionVariables tableName _adOutput
|
||||
m API.DeleteMutationOperation
|
||||
translateDelete AnnDel {..} = do
|
||||
whereExp <- translateBoolExpToExpression (API.TNTable tableName) (BoolAnd [permissionFilter, whereClause])
|
||||
returningFields <- translateMutationOutputToReturningFields tableName _adOutput
|
||||
pure
|
||||
$ API.DeleteMutationOperation
|
||||
{ API._dmoTable = tableName,
|
||||
@ -297,35 +336,37 @@ translateDelete sessionVariables AnnDel {..} = do
|
||||
(permissionFilter, whereClause) = _adWhere
|
||||
|
||||
translateMutationOutputToReturningFields ::
|
||||
( MonadError QErr m,
|
||||
Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
API.TableName ->
|
||||
MutationOutputG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (HashMap FieldName API.Field)
|
||||
translateMutationOutputToReturningFields sessionVariables tableName = \case
|
||||
m (HashMap FieldName API.Field)
|
||||
translateMutationOutputToReturningFields tableName = \case
|
||||
MOutSinglerowObject annFields ->
|
||||
translateAnnFields sessionVariables noPrefix (TableNameKey tableName) annFields
|
||||
translateAnnFields noPrefix (API.TNTable tableName) annFields
|
||||
MOutMultirowFields mutFields ->
|
||||
HashMap.unions <$> traverse (uncurry $ translateMutField sessionVariables tableName) mutFields
|
||||
HashMap.unions <$> traverse (uncurry $ translateMutField tableName) mutFields
|
||||
|
||||
translateMutField ::
|
||||
( MonadError QErr m,
|
||||
Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
API.TableName ->
|
||||
FieldName ->
|
||||
MutFldG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (HashMap FieldName API.Field)
|
||||
translateMutField sessionVariables tableName fieldName = \case
|
||||
m (HashMap FieldName API.Field)
|
||||
translateMutField tableName fieldName = \case
|
||||
MCount ->
|
||||
-- All mutation operations in a request return their affected rows count.
|
||||
-- The count can just be added to the response JSON during agent response reshaping
|
||||
@ -336,7 +377,7 @@ translateMutField sessionVariables tableName fieldName = \case
|
||||
-- to us
|
||||
pure mempty
|
||||
MRet annFields ->
|
||||
translateAnnFields sessionVariables (prefixWith fieldName) (TableNameKey tableName) annFields
|
||||
translateAnnFields (prefixWith fieldName) (API.TNTable tableName) annFields
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -13,11 +13,10 @@ where
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Monad.Trans.Writer.CPS qualified as CPS
|
||||
import Data.Aeson qualified as J
|
||||
import Data.Aeson.Encoding qualified as JE
|
||||
import Data.Bifunctor (Bifunctor (bimap))
|
||||
import Data.Has (Has)
|
||||
import Data.Has
|
||||
import Data.HashMap.Strict qualified as HashMap
|
||||
import Data.List.NonEmpty qualified as NE
|
||||
import Data.Semigroup (Min (..))
|
||||
@ -63,11 +62,14 @@ instance Monoid FieldsAndAggregates where
|
||||
-- | Map a 'QueryDB 'DataConnector' term into a 'Plan'
|
||||
mkQueryPlan ::
|
||||
forall m r.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
QueryDB 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
m (Plan API.QueryRequest API.QueryResponse)
|
||||
mkQueryPlan sessionVariables ir = do
|
||||
mkQueryPlan ir = do
|
||||
queryRequest <- translateQueryDB ir
|
||||
pure $ Plan queryRequest (reshapeResponseToQueryShape ir)
|
||||
where
|
||||
@ -76,78 +78,103 @@ mkQueryPlan sessionVariables ir = do
|
||||
m API.QueryRequest
|
||||
translateQueryDB =
|
||||
\case
|
||||
QDBMultipleRows simpleSelect -> translateAnnSimpleSelectToQueryRequest sessionVariables simpleSelect
|
||||
QDBSingleRow simpleSelect -> translateAnnSimpleSelectToQueryRequest sessionVariables simpleSelect
|
||||
QDBAggregation aggregateSelect -> translateAnnAggregateSelectToQueryRequest sessionVariables aggregateSelect
|
||||
QDBMultipleRows simpleSelect -> translateAnnSimpleSelectToQueryRequest simpleSelect
|
||||
QDBSingleRow simpleSelect -> translateAnnSimpleSelectToQueryRequest simpleSelect
|
||||
QDBAggregation aggregateSelect -> translateAnnAggregateSelectToQueryRequest aggregateSelect
|
||||
|
||||
translateAnnSimpleSelectToQueryRequest ::
|
||||
forall m r.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnSimpleSelectG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
m API.QueryRequest
|
||||
translateAnnSimpleSelectToQueryRequest sessionVariables simpleSelect =
|
||||
translateAnnSelectToQueryRequest sessionVariables (translateAnnFieldsWithNoAggregates sessionVariables noPrefix) simpleSelect
|
||||
translateAnnSimpleSelectToQueryRequest simpleSelect =
|
||||
translateAnnSelectToQueryRequest (translateAnnFieldsWithNoAggregates noPrefix) simpleSelect
|
||||
|
||||
translateAnnAggregateSelectToQueryRequest ::
|
||||
forall m r.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
AnnAggregateSelectG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
m API.QueryRequest
|
||||
translateAnnAggregateSelectToQueryRequest sessionVariables aggregateSelect =
|
||||
translateAnnSelectToQueryRequest sessionVariables (translateTableAggregateFields sessionVariables) aggregateSelect
|
||||
translateAnnAggregateSelectToQueryRequest aggregateSelect =
|
||||
translateAnnSelectToQueryRequest translateTableAggregateFields aggregateSelect
|
||||
|
||||
translateAnnSelectToQueryRequest ::
|
||||
forall m r fieldType.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
(TableRelationshipsKey -> Fields (fieldType (UnpreparedValue 'DataConnector)) -> CPS.WriterT TableRelationships m FieldsAndAggregates) ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
( forall state m2.
|
||||
( MonadState state m2,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m2,
|
||||
MonadReader r m2,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
API.TargetName ->
|
||||
Fields (fieldType (UnpreparedValue 'DataConnector)) ->
|
||||
m2 FieldsAndAggregates
|
||||
) ->
|
||||
AnnSelectG 'DataConnector fieldType (UnpreparedValue 'DataConnector) ->
|
||||
m API.QueryRequest
|
||||
translateAnnSelectToQueryRequest sessionVariables translateFieldsAndAggregates selectG = do
|
||||
translateAnnSelectToQueryRequest translateFieldsAndAggregates selectG = do
|
||||
case _asnFrom selectG of
|
||||
FromIdentifier _ -> throw400 NotSupported "AnnSelectG: FromIdentifier not supported"
|
||||
FromNativeQuery {} -> throw400 NotSupported "AnnSelectG: FromNativeQuery not supported"
|
||||
FromStoredProcedure {} -> throw400 NotSupported "AnnSelectG: FromStoredProcedure not supported"
|
||||
FromTable tableName -> do
|
||||
(query, TableRelationships tableRelationships) <-
|
||||
CPS.runWriterT (translateAnnSelect sessionVariables translateFieldsAndAggregates (TableNameKey (Witch.into tableName)) selectG)
|
||||
(query, (TableRelationships tableRelationships, redactionExpressionState)) <-
|
||||
flip runStateT (mempty, RedactionExpressionState mempty) $ translateAnnSelect translateFieldsAndAggregates (API.TNTable (Witch.into tableName)) selectG
|
||||
let relationships = mkRelationships <$> HashMap.toList tableRelationships
|
||||
pure
|
||||
$ API.QRTable
|
||||
API.TableRequest
|
||||
{ _trTable = Witch.into tableName,
|
||||
_trRelationships = Set.fromList relationships,
|
||||
_trRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_trQuery = query,
|
||||
_trForeach = Nothing
|
||||
}
|
||||
FromFunction fn@(FunctionName functionName) argsExp _dListM -> do
|
||||
args <- mkArgs sessionVariables argsExp fn
|
||||
(query, TableRelationships tableRelationships) <-
|
||||
CPS.runWriterT (translateAnnSelect sessionVariables translateFieldsAndAggregates (FunctionNameKey (Witch.into functionName)) selectG)
|
||||
args <- mkArgs argsExp fn
|
||||
(query, (redactionExpressionState, TableRelationships tableRelationships)) <-
|
||||
flip runStateT (RedactionExpressionState mempty, mempty) $ translateAnnSelect translateFieldsAndAggregates (API.TNFunction (Witch.into functionName)) selectG
|
||||
let relationships = mkRelationships <$> HashMap.toList tableRelationships
|
||||
pure
|
||||
$ API.QRFunction
|
||||
API.FunctionRequest
|
||||
{ _frFunction = Witch.into functionName,
|
||||
_frRelationships = Set.fromList relationships,
|
||||
_frRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_frQuery = query,
|
||||
_frFunctionArguments = args
|
||||
}
|
||||
|
||||
mkRelationships :: (TableRelationshipsKey, (HashMap API.RelationshipName API.Relationship)) -> API.Relationships
|
||||
mkRelationships (FunctionNameKey functionName, relationships) = API.RFunction (API.FunctionRelationships functionName relationships)
|
||||
mkRelationships (TableNameKey tableName, relationships) = API.RTable (API.TableRelationships tableName relationships)
|
||||
mkRelationships :: (API.TargetName, (HashMap API.RelationshipName API.Relationship)) -> API.Relationships
|
||||
mkRelationships (API.TNFunction functionName, relationships) = API.RFunction (API.FunctionRelationships functionName relationships)
|
||||
mkRelationships (API.TNTable tableName, relationships) = API.RTable (API.TableRelationships tableName relationships)
|
||||
|
||||
mkArgs ::
|
||||
( MonadError QErr m
|
||||
forall r m.
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
Function.FunctionArgsExpG (ArgumentExp (UnpreparedValue 'DataConnector)) ->
|
||||
FunctionName ->
|
||||
m [API.FunctionArgument]
|
||||
mkArgs sessionVariables (Function.FunctionArgsExp ps ns) functionName = do
|
||||
mkArgs (Function.FunctionArgsExp ps ns) functionName = do
|
||||
unless (null ps) $ throw400 NotSupported $ "Positional arguments not supported in function " <> toTxt functionName
|
||||
getNamed
|
||||
where
|
||||
@ -158,28 +185,31 @@ mkArgs sessionVariables (Function.FunctionArgsExp ps ns) functionName = do
|
||||
UVLiteral _ -> throw400 NotSupported "Literal not supported in Data Connector function args."
|
||||
UVSessionVar _ _ -> throw400 NotSupported "SessionVar not supported in Data Connector function args."
|
||||
UVParameter _ (ColumnValue t v) -> pure (API.ScalarValue v (coerce (toTxt t)))
|
||||
UVSession -> pure (API.ScalarValue (J.toJSON sessionVariables) (API.ScalarType "json"))
|
||||
UVSession -> do
|
||||
(sessionVariables :: SessionVariables) <- asks getter
|
||||
pure (API.ScalarValue (J.toJSON sessionVariables) (API.ScalarType "json"))
|
||||
|
||||
translateAnnSelect ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
(TableRelationshipsKey -> Fields (fieldType (UnpreparedValue 'DataConnector)) -> CPS.WriterT writerOutput m FieldsAndAggregates) ->
|
||||
TableRelationshipsKey ->
|
||||
(API.TargetName -> Fields (fieldType (UnpreparedValue 'DataConnector)) -> m FieldsAndAggregates) ->
|
||||
API.TargetName ->
|
||||
AnnSelectG 'DataConnector fieldType (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m API.Query
|
||||
translateAnnSelect sessionVariables translateFieldsAndAggregates entityName selectG = do
|
||||
m API.Query
|
||||
translateAnnSelect translateFieldsAndAggregates entityName selectG = do
|
||||
FieldsAndAggregates {..} <- translateFieldsAndAggregates entityName (_asnFields selectG)
|
||||
let whereClauseWithPermissions =
|
||||
case _saWhere (_asnArgs selectG) of
|
||||
Just expr -> BoolAnd [expr, _tpFilter (_asnPerm selectG)]
|
||||
Nothing -> _tpFilter (_asnPerm selectG)
|
||||
whereClause <- translateBoolExpToExpression sessionVariables entityName whereClauseWithPermissions
|
||||
orderBy <- traverse (translateOrderBy sessionVariables entityName) (_saOrderBy $ _asnArgs selectG)
|
||||
whereClause <- translateBoolExpToExpression entityName whereClauseWithPermissions
|
||||
orderBy <- traverse (translateOrderBy entityName) (_saOrderBy $ _asnArgs selectG)
|
||||
pure
|
||||
API.Query
|
||||
{ _qFields = mapFieldNameHashMap <$> _faaFields,
|
||||
@ -198,21 +228,22 @@ translateAnnSelect sessionVariables translateFieldsAndAggregates entityName sele
|
||||
}
|
||||
|
||||
translateOrderBy ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
NE.NonEmpty (AnnotatedOrderByItemG 'DataConnector (UnpreparedValue 'DataConnector)) ->
|
||||
CPS.WriterT writerOutput m API.OrderBy
|
||||
translateOrderBy sessionVariables sourceName orderByItems = do
|
||||
m API.OrderBy
|
||||
translateOrderBy sourceName orderByItems = do
|
||||
orderByElementsAndRelations <- for orderByItems \OrderByItemG {..} -> do
|
||||
let orderDirection = maybe API.Ascending Witch.from obiType
|
||||
translateOrderByElement sessionVariables sourceName orderDirection [] obiColumn
|
||||
relations <- lift . mergeOrderByRelations $ snd <$> orderByElementsAndRelations
|
||||
translateOrderByElement sourceName orderDirection [] obiColumn
|
||||
relations <- mergeOrderByRelations $ snd <$> orderByElementsAndRelations
|
||||
pure
|
||||
API.OrderBy
|
||||
{ _obRelations = relations,
|
||||
@ -220,34 +251,35 @@ translateOrderBy sessionVariables sourceName orderByItems = do
|
||||
}
|
||||
|
||||
translateOrderByElement ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
API.OrderDirection ->
|
||||
[API.RelationshipName] ->
|
||||
AnnotatedOrderByElement 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (API.OrderByElement, HashMap API.RelationshipName API.OrderByRelation)
|
||||
translateOrderByElement sessionVariables sourceName orderDirection targetReversePath = \case
|
||||
-- TODO(redactionExp): Deal with this redaction expressions
|
||||
AOCColumn ColumnInfo {..} _redactionExp ->
|
||||
m (API.OrderByElement, HashMap API.RelationshipName API.OrderByRelation)
|
||||
translateOrderByElement sourceName orderDirection targetReversePath = \case
|
||||
AOCColumn ColumnInfo {..} redactionExp -> do
|
||||
redactionExpName <- recordRedactionExpression sourceName redactionExp
|
||||
pure
|
||||
( API.OrderByElement
|
||||
{ _obeTargetPath = reverse targetReversePath,
|
||||
_obeTarget = API.OrderByColumn $ Witch.from ciColumn,
|
||||
_obeTarget = API.OrderByColumn (Witch.from ciColumn) redactionExpName,
|
||||
_obeOrderDirection = orderDirection
|
||||
},
|
||||
mempty
|
||||
)
|
||||
AOCObjectRelation relationshipInfo filterExp orderByElement -> do
|
||||
(relationshipName, API.Relationship {..}) <- recordTableRelationshipFromRelInfo sourceName relationshipInfo
|
||||
(translatedOrderByElement, subOrderByRelations) <- translateOrderByElement sessionVariables (TableNameKey _rTargetTable) orderDirection (relationshipName : targetReversePath) orderByElement
|
||||
(translatedOrderByElement, subOrderByRelations) <- translateOrderByElement (API.TNTable _rTargetTable) orderDirection (relationshipName : targetReversePath) orderByElement
|
||||
|
||||
targetTableWhereExp <- translateBoolExpToExpression sessionVariables (TableNameKey _rTargetTable) filterExp
|
||||
targetTableWhereExp <- translateBoolExpToExpression (API.TNTable _rTargetTable) filterExp
|
||||
let orderByRelations = HashMap.fromList [(relationshipName, API.OrderByRelation targetTableWhereExp subOrderByRelations)]
|
||||
|
||||
pure (translatedOrderByElement, orderByRelations)
|
||||
@ -257,10 +289,10 @@ translateOrderByElement sessionVariables sourceName orderDirection targetReverse
|
||||
AAOCount ->
|
||||
pure API.OrderByStarCountAggregate
|
||||
AAOOp AggregateOrderByColumn {_aobcColumn = ColumnInfo {..}, ..} -> do
|
||||
-- TODO(redactionExp): Deal with the redaction expression: _aobcRedactionExpression
|
||||
aggFunction <- lift $ translateSingleColumnAggregateFunction _aobcAggregateFunctionName
|
||||
redactionExpName <- recordRedactionExpression sourceName _aobcRedactionExpression
|
||||
aggFunction <- translateSingleColumnAggregateFunction _aobcAggregateFunctionName
|
||||
let resultScalarType = Witch.from $ columnTypeToScalarType _aobcAggregateFunctionReturnType
|
||||
pure . API.OrderBySingleColumnAggregate $ API.SingleColumnAggregate aggFunction (Witch.from ciColumn) resultScalarType
|
||||
pure . API.OrderBySingleColumnAggregate $ API.SingleColumnAggregate aggFunction (Witch.from ciColumn) redactionExpName resultScalarType
|
||||
|
||||
let translatedOrderByElement =
|
||||
API.OrderByElement
|
||||
@ -269,7 +301,7 @@ translateOrderByElement sessionVariables sourceName orderDirection targetReverse
|
||||
_obeOrderDirection = orderDirection
|
||||
}
|
||||
|
||||
targetTableWhereExp <- translateBoolExpToExpression sessionVariables (TableNameKey _rTargetTable) filterExp
|
||||
targetTableWhereExp <- translateBoolExpToExpression (API.TNTable _rTargetTable) filterExp
|
||||
let orderByRelations = HashMap.fromList [(relationshipName, API.OrderByRelation targetTableWhereExp mempty)]
|
||||
pure (translatedOrderByElement, orderByRelations)
|
||||
|
||||
@ -293,71 +325,73 @@ mergeOrderByRelations orderByRelationsList =
|
||||
else throw500 "mergeOrderByRelations: Differing filter expressions found for the same table"
|
||||
|
||||
translateAnnFieldsWithNoAggregates ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
FieldPrefix ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
AnnFieldsG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m FieldsAndAggregates
|
||||
translateAnnFieldsWithNoAggregates sessionVariables fieldNamePrefix sourceName fields =
|
||||
(\fields' -> FieldsAndAggregates (Just fields') Nothing) <$> translateAnnFields sessionVariables fieldNamePrefix sourceName fields
|
||||
m FieldsAndAggregates
|
||||
translateAnnFieldsWithNoAggregates fieldNamePrefix sourceName fields =
|
||||
(\fields' -> FieldsAndAggregates (Just fields') Nothing) <$> translateAnnFields fieldNamePrefix sourceName fields
|
||||
|
||||
translateAnnFields ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
FieldPrefix ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
AnnFieldsG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (HashMap FieldName API.Field)
|
||||
translateAnnFields sessionVariables fieldNamePrefix sourceName fields = do
|
||||
translatedFields <- traverse (traverse (translateAnnField sessionVariables sourceName)) fields
|
||||
m (HashMap FieldName API.Field)
|
||||
translateAnnFields fieldNamePrefix sourceName fields = do
|
||||
translatedFields <- traverse (traverse (translateAnnField sourceName)) fields
|
||||
pure $ HashMap.fromList (mapMaybe (\(fieldName, field) -> (applyPrefix fieldNamePrefix fieldName,) <$> field) translatedFields)
|
||||
|
||||
translateAnnField ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
AnnFieldG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m (Maybe API.Field)
|
||||
translateAnnField sessionVariables sourceTableName = \case
|
||||
m (Maybe API.Field)
|
||||
translateAnnField sourceTableName = \case
|
||||
AFNestedObject nestedObj ->
|
||||
Just
|
||||
. API.NestedObjField (Witch.from $ _anosColumn nestedObj)
|
||||
<$> translateNestedObjectSelect sessionVariables sourceTableName nestedObj
|
||||
<$> translateNestedObjectSelect sourceTableName nestedObj
|
||||
AFNestedArray _ (ANASSimple field) ->
|
||||
fmap mkArrayField <$> translateAnnField sessionVariables sourceTableName field
|
||||
fmap mkArrayField <$> translateAnnField sourceTableName field
|
||||
where
|
||||
mkArrayField nestedField =
|
||||
API.NestedArrayField (API.ArrayField nestedField Nothing Nothing Nothing Nothing)
|
||||
-- TODO(dmoverton): support limit, offset, where and order_by in ArrayField
|
||||
AFNestedArray _ (ANASAggregate _) ->
|
||||
pure Nothing -- TODO(dmoverton): support nested array aggregates
|
||||
AFColumn colField ->
|
||||
-- TODO(redactionExp): Deal with the redaction expression: _acfRedactionExpression
|
||||
-- TODO: make sure certain fields in colField are not in use, since we don't support them
|
||||
pure . Just $ API.ColumnField (Witch.from $ _acfColumn colField) (Witch.from . columnTypeToScalarType $ _acfType colField)
|
||||
AFColumn AnnColumnField {..} -> do
|
||||
redactionExpName <- recordRedactionExpression sourceTableName _acfRedactionExpression
|
||||
pure . Just $ API.ColumnField (Witch.from _acfColumn) (Witch.from $ columnTypeToScalarType _acfType) redactionExpName
|
||||
AFObjectRelation objRel ->
|
||||
case _aosTarget (_aarAnnSelect objRel) of
|
||||
FromTable tableName -> do
|
||||
let targetTable = Witch.from tableName
|
||||
let relationshipName = mkRelationshipName $ _aarRelationshipName objRel
|
||||
fields <- translateAnnFields sessionVariables noPrefix (TableNameKey targetTable) (_aosFields (_aarAnnSelect objRel))
|
||||
whereClause <- translateBoolExpToExpression sessionVariables (TableNameKey targetTable) (_aosTargetFilter (_aarAnnSelect objRel))
|
||||
fields <- translateAnnFields noPrefix (API.TNTable targetTable) (_aosFields (_aarAnnSelect objRel))
|
||||
whereClause <- translateBoolExpToExpression (API.TNTable targetTable) (_aosTargetFilter (_aarAnnSelect objRel))
|
||||
|
||||
recordTableRelationship
|
||||
sourceTableName
|
||||
@ -385,9 +419,9 @@ translateAnnField sessionVariables sourceTableName = \case
|
||||
)
|
||||
other -> error $ "translateAnnField: " <> show other
|
||||
AFArrayRelation (ASSimple arrayRelationSelect) -> do
|
||||
Just <$> translateArrayRelationSelect sessionVariables sourceTableName (translateAnnFieldsWithNoAggregates sessionVariables noPrefix) arrayRelationSelect
|
||||
Just <$> translateArrayRelationSelect sourceTableName (translateAnnFieldsWithNoAggregates noPrefix) arrayRelationSelect
|
||||
AFArrayRelation (ASAggregate arrayRelationSelect) ->
|
||||
Just <$> translateArrayRelationSelect sessionVariables sourceTableName (translateTableAggregateFields sessionVariables) arrayRelationSelect
|
||||
Just <$> translateArrayRelationSelect sourceTableName translateTableAggregateFields arrayRelationSelect
|
||||
AFExpression _literal ->
|
||||
-- We ignore literal text fields (we don't send them to the data connector agent)
|
||||
-- and add them back to the response JSON when we reshape what the agent returns
|
||||
@ -395,25 +429,26 @@ translateAnnField sessionVariables sourceTableName = \case
|
||||
pure Nothing
|
||||
|
||||
translateArrayRelationSelect ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
(TableRelationshipsKey -> Fields (fieldType (UnpreparedValue 'DataConnector)) -> CPS.WriterT writerOutput m FieldsAndAggregates) ->
|
||||
API.TargetName ->
|
||||
(API.TargetName -> Fields (fieldType (UnpreparedValue 'DataConnector)) -> m FieldsAndAggregates) ->
|
||||
AnnRelationSelectG 'DataConnector (AnnSelectG 'DataConnector fieldType (UnpreparedValue 'DataConnector)) ->
|
||||
CPS.WriterT writerOutput m API.Field
|
||||
translateArrayRelationSelect sessionVariables sourceName translateFieldsAndAggregates arrRel = do
|
||||
m API.Field
|
||||
translateArrayRelationSelect sourceName translateFieldsAndAggregates arrRel = do
|
||||
case _asnFrom (_aarAnnSelect arrRel) of
|
||||
FromIdentifier _ -> lift $ throw400 NotSupported "AnnSelectG: FromIdentifier not supported"
|
||||
FromNativeQuery {} -> lift $ throw400 NotSupported "AnnSelectG: FromNativeQuery not supported"
|
||||
FromStoredProcedure {} -> lift $ throw400 NotSupported "AnnSelectG: FromStoredProcedure not supported"
|
||||
FromFunction {} -> lift $ throw400 NotSupported "translateArrayRelationSelect: FromFunction not currently supported"
|
||||
FromIdentifier _ -> throw400 NotSupported "AnnSelectG: FromIdentifier not supported"
|
||||
FromNativeQuery {} -> throw400 NotSupported "AnnSelectG: FromNativeQuery not supported"
|
||||
FromStoredProcedure {} -> throw400 NotSupported "AnnSelectG: FromStoredProcedure not supported"
|
||||
FromFunction {} -> throw400 NotSupported "translateArrayRelationSelect: FromFunction not currently supported"
|
||||
FromTable targetTable -> do
|
||||
query <- translateAnnSelect sessionVariables translateFieldsAndAggregates (TableNameKey (Witch.into targetTable)) (_aarAnnSelect arrRel)
|
||||
query <- translateAnnSelect translateFieldsAndAggregates (API.TNTable (Witch.into targetTable)) (_aarAnnSelect arrRel)
|
||||
let relationshipName = mkRelationshipName $ _aarRelationshipName arrRel
|
||||
|
||||
recordTableRelationship
|
||||
@ -432,41 +467,43 @@ translateArrayRelationSelect sessionVariables sourceName translateFieldsAndAggre
|
||||
query
|
||||
|
||||
translateTableAggregateFields ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
TableAggregateFieldsG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m FieldsAndAggregates
|
||||
translateTableAggregateFields sessionVariables sourceName fields = do
|
||||
mconcat <$> traverse (uncurry (translateTableAggregateField sessionVariables sourceName)) fields
|
||||
m FieldsAndAggregates
|
||||
translateTableAggregateFields sourceName fields = do
|
||||
mconcat <$> traverse (uncurry (translateTableAggregateField sourceName)) fields
|
||||
|
||||
translateTableAggregateField ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
FieldName ->
|
||||
TableAggregateFieldG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m FieldsAndAggregates
|
||||
translateTableAggregateField sessionVariables sourceName fieldName = \case
|
||||
m FieldsAndAggregates
|
||||
translateTableAggregateField sourceName fieldName = \case
|
||||
TAFAgg aggregateFields -> do
|
||||
let fieldNamePrefix = prefixWith fieldName
|
||||
translatedAggregateFields <- lift $ mconcat <$> traverse (uncurry (translateAggregateField fieldNamePrefix)) aggregateFields
|
||||
translatedAggregateFields <- mconcat <$> traverse (uncurry (translateAggregateField sourceName fieldNamePrefix)) aggregateFields
|
||||
pure
|
||||
$ FieldsAndAggregates
|
||||
Nothing
|
||||
(Just translatedAggregateFields)
|
||||
TAFNodes _ fields ->
|
||||
translateAnnFieldsWithNoAggregates sessionVariables (prefixWith fieldName) sourceName fields
|
||||
translateAnnFieldsWithNoAggregates (prefixWith fieldName) sourceName fields
|
||||
TAFExp _txt ->
|
||||
-- We ignore literal text fields (we don't send them to the data connector agent)
|
||||
-- and add them back to the response JSON when we reshape what the agent returns
|
||||
@ -474,30 +511,41 @@ translateTableAggregateField sessionVariables sourceName fieldName = \case
|
||||
pure mempty
|
||||
|
||||
translateAggregateField ::
|
||||
(MonadError QErr m) =>
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
API.TargetName ->
|
||||
FieldPrefix ->
|
||||
FieldName ->
|
||||
AggregateField 'DataConnector (UnpreparedValue 'DataConnector) ->
|
||||
m (HashMap FieldName API.Aggregate)
|
||||
translateAggregateField fieldPrefix fieldName = \case
|
||||
AFCount countAggregate ->
|
||||
let aggregate =
|
||||
case countAggregate of
|
||||
StarCount -> API.StarCount
|
||||
-- TODO(redactionExp): Do something with these redaction expressions
|
||||
ColumnCount (column, _redactionExp) -> API.ColumnCount $ API.ColumnCountAggregate {_ccaColumn = Witch.from column, _ccaDistinct = False}
|
||||
ColumnDistinctCount (column, _redactionExp) -> API.ColumnCount $ API.ColumnCountAggregate {_ccaColumn = Witch.from column, _ccaDistinct = True}
|
||||
in pure $ HashMap.singleton (applyPrefix fieldPrefix fieldName) aggregate
|
||||
translateAggregateField sourceName fieldPrefix fieldName = \case
|
||||
AFCount countAggregate -> do
|
||||
aggregate <-
|
||||
case countAggregate of
|
||||
StarCount -> pure API.StarCount
|
||||
ColumnCount (column, redactionExp) -> do
|
||||
redactionExpName <- recordRedactionExpression sourceName redactionExp
|
||||
pure $ API.ColumnCount $ API.ColumnCountAggregate {_ccaColumn = Witch.from column, _ccaRedactionExpression = redactionExpName, _ccaDistinct = False}
|
||||
ColumnDistinctCount (column, redactionExp) -> do
|
||||
redactionExpName <- recordRedactionExpression sourceName redactionExp
|
||||
pure $ API.ColumnCount $ API.ColumnCountAggregate {_ccaColumn = Witch.from column, _ccaRedactionExpression = redactionExpName, _ccaDistinct = True}
|
||||
pure $ HashMap.singleton (applyPrefix fieldPrefix fieldName) aggregate
|
||||
AFOp AggregateOp {..} -> do
|
||||
let fieldPrefix' = fieldPrefix <> prefixWith fieldName
|
||||
aggFunction <- translateSingleColumnAggregateFunction _aoOp
|
||||
|
||||
fmap (HashMap.fromList . catMaybes) . forM _aoFields $ \(columnFieldName, columnField) ->
|
||||
case columnField of
|
||||
-- TODO(redactionExp): Do something with the redactionExp
|
||||
SFCol column resultType _redactionExp ->
|
||||
SFCol column resultType redactionExp -> do
|
||||
redactionExpName <- recordRedactionExpression sourceName redactionExp
|
||||
let resultScalarType = Witch.from $ columnTypeToScalarType resultType
|
||||
in pure . Just $ (applyPrefix fieldPrefix' columnFieldName, API.SingleColumn $ API.SingleColumnAggregate aggFunction (Witch.from column) resultScalarType)
|
||||
pure $ Just (applyPrefix fieldPrefix' columnFieldName, API.SingleColumn $ API.SingleColumnAggregate aggFunction (Witch.from column) redactionExpName resultScalarType)
|
||||
SFExp _txt ->
|
||||
-- We ignore literal text fields (we don't send them to the data connector agent)
|
||||
-- and add them back to the response JSON when we reshape what the agent returns
|
||||
@ -517,18 +565,19 @@ translateSingleColumnAggregateFunction functionName =
|
||||
`onNothing` throw500 ("translateSingleColumnAggregateFunction: Invalid aggregate function encountered: " <> functionName)
|
||||
|
||||
translateNestedObjectSelect ::
|
||||
( Has TableRelationships writerOutput,
|
||||
Monoid writerOutput,
|
||||
( MonadState state m,
|
||||
Has TableRelationships state,
|
||||
Has RedactionExpressionState state,
|
||||
MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SessionVariables ->
|
||||
TableRelationshipsKey ->
|
||||
API.TargetName ->
|
||||
AnnNestedObjectSelectG 'DataConnector Void (UnpreparedValue 'DataConnector) ->
|
||||
CPS.WriterT writerOutput m API.Query
|
||||
translateNestedObjectSelect sessionVariables relationshipKey selectG = do
|
||||
FieldsAndAggregates {..} <- translateAnnFieldsWithNoAggregates sessionVariables noPrefix relationshipKey $ _anosFields selectG
|
||||
m API.Query
|
||||
translateNestedObjectSelect relationshipKey selectG = do
|
||||
FieldsAndAggregates {..} <- translateAnnFieldsWithNoAggregates noPrefix relationshipKey $ _anosFields selectG
|
||||
pure
|
||||
API.Query
|
||||
{ _qFields = mapFieldNameHashMap <$> _faaFields,
|
||||
|
@ -6,7 +6,6 @@ where
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
import Control.Lens ((?~))
|
||||
import Control.Monad.Trans.Writer.CPS qualified as CPS
|
||||
import Data.Aeson qualified as J
|
||||
import Data.Aeson.Encoding qualified as JE
|
||||
import Data.Aeson.Key qualified as K
|
||||
@ -34,8 +33,11 @@ import Witch qualified
|
||||
|
||||
mkRemoteRelationshipPlan ::
|
||||
forall m r.
|
||||
(MonadError QErr m, MonadReader r m, Has API.ScalarTypesCapabilities r) =>
|
||||
SessionVariables ->
|
||||
( MonadError QErr m,
|
||||
MonadReader r m,
|
||||
Has API.ScalarTypesCapabilities r,
|
||||
Has SessionVariables r
|
||||
) =>
|
||||
SourceConfig ->
|
||||
-- | List of join json objects, each of which contains IDs to be laterally-joined against
|
||||
-- as well as an argument ID that identifies the particular set of IDs (ie 'row' to join against).
|
||||
@ -52,7 +54,7 @@ mkRemoteRelationshipPlan ::
|
||||
FieldName ->
|
||||
SourceRelationshipSelection 'DataConnector Void UnpreparedValue ->
|
||||
m (Plan API.QueryRequest API.QueryResponse)
|
||||
mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema argumentIdFieldName resultFieldName ir = do
|
||||
mkRemoteRelationshipPlan _sourceConfig joinIds joinIdsSchema argumentIdFieldName resultFieldName ir = do
|
||||
foreachRowFilter <- traverse (translateForeachRowFilter argumentIdFieldName joinIdsSchema) joinIds
|
||||
argumentIds <- extractArgumentIds argumentIdFieldName joinIds
|
||||
queryRequest <- translateSourceRelationshipSelection foreachRowFilter ir
|
||||
@ -66,10 +68,10 @@ mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema ar
|
||||
SourceRelationshipObject objectSelect ->
|
||||
translateAnnObjectSelectToQueryRequest foreachRowFilter objectSelect
|
||||
SourceRelationshipArray simpleSelect ->
|
||||
QueryPlan.translateAnnSimpleSelectToQueryRequest sessionVariables simpleSelect
|
||||
QueryPlan.translateAnnSimpleSelectToQueryRequest simpleSelect
|
||||
<&> (API.qrForeach ?~ foreachRowFilter)
|
||||
SourceRelationshipArrayAggregate aggregateSelect ->
|
||||
QueryPlan.translateAnnAggregateSelectToQueryRequest sessionVariables aggregateSelect
|
||||
QueryPlan.translateAnnAggregateSelectToQueryRequest aggregateSelect
|
||||
<&> (API.qrForeach ?~ foreachRowFilter)
|
||||
|
||||
translateAnnObjectSelectToQueryRequest ::
|
||||
@ -80,16 +82,18 @@ mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema ar
|
||||
let tableName = case _aosTarget of
|
||||
FromTable table -> Witch.from table
|
||||
other -> error $ "translateAnnObjectSelectToQueryRequest: " <> show other
|
||||
((fields, whereClause), (TableRelationships tableRelationships)) <- CPS.runWriterT $ do
|
||||
fields <- QueryPlan.translateAnnFields sessionVariables noPrefix (TableNameKey tableName) _aosFields
|
||||
whereClause <- translateBoolExpToExpression sessionVariables (TableNameKey tableName) _aosTargetFilter
|
||||
pure (fields, whereClause)
|
||||
((fields, whereClause), (TableRelationships tableRelationships, redactionExpressionState)) <-
|
||||
flip runStateT (mempty, RedactionExpressionState mempty) do
|
||||
fields <- QueryPlan.translateAnnFields noPrefix (API.TNTable tableName) _aosFields
|
||||
whereClause <- translateBoolExpToExpression (API.TNTable tableName) _aosTargetFilter
|
||||
pure (fields, whereClause)
|
||||
let apiTableRelationships = Set.fromList $ tableRelationshipsToList tableRelationships
|
||||
pure
|
||||
$ API.QRTable
|
||||
$ API.TableRequest
|
||||
{ _trTable = tableName,
|
||||
_trRelationships = apiTableRelationships,
|
||||
_trRedactionExpressions = translateRedactionExpressions redactionExpressionState,
|
||||
_trQuery =
|
||||
API.Query
|
||||
{ _qFields = Just $ mapFieldNameHashMap fields,
|
||||
@ -103,12 +107,12 @@ mkRemoteRelationshipPlan sessionVariables _sourceConfig joinIds joinIdsSchema ar
|
||||
_trForeach = Just foreachRowFilter
|
||||
}
|
||||
|
||||
tableRelationshipsToList :: HashMap TableRelationshipsKey (HashMap API.RelationshipName API.Relationship) -> [API.Relationships]
|
||||
tableRelationshipsToList :: HashMap API.TargetName (HashMap API.RelationshipName API.Relationship) -> [API.Relationships]
|
||||
tableRelationshipsToList m = map (either (API.RFunction . uncurry API.FunctionRelationships) (API.RTable . uncurry API.TableRelationships) . tableRelationshipsKeyToEither) (HashMap.toList m)
|
||||
|
||||
tableRelationshipsKeyToEither :: (TableRelationshipsKey, c) -> Either (API.FunctionName, c) (API.TableName, c)
|
||||
tableRelationshipsKeyToEither (FunctionNameKey f, x) = Left (f, x)
|
||||
tableRelationshipsKeyToEither (TableNameKey t, x) = Right (t, x)
|
||||
tableRelationshipsKeyToEither :: (API.TargetName, c) -> Either (API.FunctionName, c) (API.TableName, c)
|
||||
tableRelationshipsKeyToEither (API.TNFunction f, x) = Left (f, x)
|
||||
tableRelationshipsKeyToEither (API.TNTable t, x) = Right (t, x)
|
||||
|
||||
translateForeachRowFilter :: (MonadError QErr m) => FieldName -> HashMap FieldName (ColumnName, ScalarType) -> J.Object -> m (HashMap API.ColumnName API.ScalarValue)
|
||||
translateForeachRowFilter argumentIdFieldName joinIdsSchema joinIds =
|
||||
|
@ -11,6 +11,7 @@ where
|
||||
import Data.Aeson.QQ.Simple (aesonQQ)
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.ColumnSpec (genColumnName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genRedactionExpressionName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ScalarSpec (genScalarType)
|
||||
import Hasura.Prelude
|
||||
import Hedgehog
|
||||
@ -25,20 +26,22 @@ spec = do
|
||||
describe "Aggregate" $ do
|
||||
describe "SingleColumn" $ do
|
||||
testToFromJSONToSchema
|
||||
(SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|avg|]) (ColumnName "my_column_name") (ScalarType "number"))
|
||||
(SingleColumn $ SingleColumnAggregate (SingleColumnAggregateFunction [G.name|avg|]) (ColumnName "my_column_name") (Just $ RedactionExpressionName "RedactionExp2") (ScalarType "number"))
|
||||
[aesonQQ|
|
||||
{ "type": "single_column",
|
||||
"function": "avg",
|
||||
"column": "my_column_name",
|
||||
"redaction_expression": "RedactionExp2",
|
||||
"result_type": "number"
|
||||
}
|
||||
|]
|
||||
describe "ColumnCount" $ do
|
||||
testToFromJSONToSchema
|
||||
(ColumnCount $ ColumnCountAggregate (ColumnName "my_column_name") True)
|
||||
(ColumnCount $ ColumnCountAggregate (ColumnName "my_column_name") (Just $ RedactionExpressionName "RedactionExp2") True)
|
||||
[aesonQQ|
|
||||
{ "type": "column_count",
|
||||
"column": "my_column_name",
|
||||
"redaction_expression": "RedactionExp2",
|
||||
"distinct": true
|
||||
}
|
||||
|]
|
||||
@ -68,12 +71,14 @@ genSingleColumnAggregate =
|
||||
SingleColumnAggregate
|
||||
<$> genSingleColumnAggregateFunction
|
||||
<*> genColumnName
|
||||
<*> Gen.maybe genRedactionExpressionName
|
||||
<*> genScalarType
|
||||
|
||||
genColumnCountAggregate :: Gen ColumnCountAggregate
|
||||
genColumnCountAggregate =
|
||||
ColumnCountAggregate
|
||||
<$> genColumnName
|
||||
<*> Gen.maybe genRedactionExpressionName
|
||||
<*> Gen.bool
|
||||
|
||||
genSingleColumnAggregateFunction :: Gen SingleColumnAggregateFunction
|
||||
|
@ -69,11 +69,14 @@ genColumnNullability =
|
||||
Gen.element [NullableAndNonNullableColumns, OnlyNullableColumns]
|
||||
|
||||
genQueryCapabilities :: (MonadGen m) => m QueryCapabilities
|
||||
genQueryCapabilities = QueryCapabilities <$> Gen.maybe genForeachCapabilities
|
||||
genQueryCapabilities = QueryCapabilities <$> Gen.maybe genForeachCapabilities <*> Gen.maybe genRedactionCapabilities
|
||||
|
||||
genForeachCapabilities :: (MonadGen m) => m ForeachCapabilities
|
||||
genForeachCapabilities = pure ForeachCapabilities
|
||||
|
||||
genRedactionCapabilities :: (MonadGen m) => m RedactionCapabilities
|
||||
genRedactionCapabilities = pure RedactionCapabilities
|
||||
|
||||
genMutationCapabilities :: (MonadGen m) => m MutationCapabilities
|
||||
genMutationCapabilities =
|
||||
MutationCapabilities
|
||||
|
@ -8,6 +8,8 @@ module Hasura.Backends.DataConnector.API.V0.ExpressionSpec
|
||||
genUnaryComparisonOperator,
|
||||
genComparisonValue,
|
||||
genExpression,
|
||||
genRedactionExpressionName,
|
||||
genTargetRedactionExpressions,
|
||||
)
|
||||
where
|
||||
|
||||
@ -18,7 +20,8 @@ import Hasura.Backends.DataConnector.API.V0.ColumnSpec (genColumnName)
|
||||
import Hasura.Backends.DataConnector.API.V0.RelationshipsSpec (genRelationshipName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ScalarSpec (genScalarType, genScalarValue)
|
||||
import Hasura.Backends.DataConnector.API.V0.TableSpec (genTableName)
|
||||
import Hasura.Generator.Common (defaultRange, genArbitraryAlphaNumTextExcluding)
|
||||
import Hasura.Backends.DataConnector.API.V0.TargetSpec (genTargetName)
|
||||
import Hasura.Generator.Common (defaultRange, genArbitraryAlphaNumText, genArbitraryAlphaNumTextExcluding, genHashMap)
|
||||
import Hasura.Prelude
|
||||
import Hedgehog
|
||||
import Hedgehog.Gen qualified as Gen
|
||||
@ -68,8 +71,8 @@ spec = do
|
||||
|
||||
describe "ComparisonColumn" $ do
|
||||
testToFromJSONToSchema
|
||||
(ComparisonColumn QueryTable (mkColumnSelector $ ColumnName "column_name") (ScalarType "string"))
|
||||
[aesonQQ|{"path": ["$"], "name": "column_name", "column_type": "string"}|]
|
||||
(ComparisonColumn QueryTable (mkColumnSelector $ ColumnName "column_name") (ScalarType "string") (Just $ RedactionExpressionName "RedactionExp1"))
|
||||
[aesonQQ|{"path": ["$"], "name": "column_name", "column_type": "string", "redaction_expression": "RedactionExp1"}|]
|
||||
|
||||
jsonOpenApiProperties genComparisonColumn
|
||||
|
||||
@ -90,7 +93,7 @@ spec = do
|
||||
describe "ComparisonValue" $ do
|
||||
describe "AnotherColumnComparison"
|
||||
$ testToFromJSONToSchema
|
||||
(AnotherColumnComparison $ ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "my_column_name") (ScalarType "string"))
|
||||
(AnotherColumnComparison $ ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "my_column_name") (ScalarType "string") Nothing)
|
||||
[aesonQQ|{"type": "column", "column": {"name": "my_column_name", "column_type": "string"}}|]
|
||||
describe "ScalarValueComparison"
|
||||
$ testToFromJSONToSchema
|
||||
@ -119,7 +122,7 @@ spec = do
|
||||
jsonOpenApiProperties genExistsInTable
|
||||
|
||||
describe "Expression" $ do
|
||||
let comparisonColumn = ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "my_column_name") (ScalarType "string")
|
||||
let comparisonColumn = ComparisonColumn CurrentTable (mkColumnSelector $ ColumnName "my_column_name") (ScalarType "string") Nothing
|
||||
let scalarValue = ScalarValueComparison $ ScalarValue (String "scalar value") (ScalarType "string")
|
||||
let scalarValues = [String "scalar value"]
|
||||
let unaryComparisonExpression = ApplyUnaryComparisonOperator IsNull comparisonColumn
|
||||
@ -226,6 +229,30 @@ spec = do
|
||||
|
||||
jsonOpenApiProperties genExpression
|
||||
|
||||
describe "RedactionExpressionName" $ do
|
||||
testToFromJSONToSchema (RedactionExpressionName "foo") [aesonQQ|"foo"|]
|
||||
jsonOpenApiProperties genRedactionExpressionName
|
||||
|
||||
describe "TargetRedactionExpressions" $ do
|
||||
testToFromJSONToSchema
|
||||
(TargetRedactionExpressions (TNTable $ TableName ["my_table_name"]) mempty)
|
||||
[aesonQQ|
|
||||
{ "target": { "type": "table", "table": ["my_table_name"] },
|
||||
"expressions": {}
|
||||
}
|
||||
|]
|
||||
jsonOpenApiProperties genTargetRedactionExpressions
|
||||
|
||||
describe "RedactionExpression" $ do
|
||||
testToFromJSONToSchema
|
||||
(RedactionExpression $ And [])
|
||||
[aesonQQ|
|
||||
{ "type": "and",
|
||||
"expressions": []
|
||||
}
|
||||
|]
|
||||
jsonOpenApiProperties genRedactionExpression
|
||||
|
||||
genBinaryComparisonOperator :: (MonadGen m, GenBase m ~ Identity) => m BinaryComparisonOperator
|
||||
genBinaryComparisonOperator =
|
||||
Gen.choice
|
||||
@ -256,6 +283,7 @@ genComparisonColumn =
|
||||
<$> genColumnPath
|
||||
<*> genColumnSelector
|
||||
<*> genScalarType
|
||||
<*> Gen.maybe genRedactionExpressionName
|
||||
|
||||
genColumnPath :: (MonadGen m) => m ColumnPath
|
||||
genColumnPath =
|
||||
@ -294,3 +322,15 @@ genExpression =
|
||||
]
|
||||
where
|
||||
genExpressions = Gen.set defaultRange genExpression
|
||||
|
||||
genRedactionExpressionName :: (MonadGen m) => m RedactionExpressionName
|
||||
genRedactionExpressionName = RedactionExpressionName <$> genArbitraryAlphaNumText defaultRange
|
||||
|
||||
genTargetRedactionExpressions :: (MonadGen m, GenBase m ~ Identity) => m TargetRedactionExpressions
|
||||
genTargetRedactionExpressions =
|
||||
TargetRedactionExpressions
|
||||
<$> genTargetName
|
||||
<*> genHashMap genRedactionExpressionName genRedactionExpression defaultRange
|
||||
|
||||
genRedactionExpression :: (MonadGen m, GenBase m ~ Identity) => m RedactionExpression
|
||||
genRedactionExpression = RedactionExpression <$> genExpression
|
||||
|
@ -0,0 +1,42 @@
|
||||
{-# LANGUAGE OverloadedLists #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Hasura.Backends.DataConnector.API.V0.FunctionSpec
|
||||
( spec,
|
||||
genFunctionName,
|
||||
genFunctionArgument,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson.QQ.Simple (aesonQQ)
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.ScalarSpec (genScalarType)
|
||||
import Hasura.Generator.Common (defaultRange, genArbitraryAlphaNumText)
|
||||
import Hasura.Prelude
|
||||
import Hedgehog
|
||||
import Hedgehog.Gen qualified as Gen
|
||||
import Hedgehog.Internal.Range (linear)
|
||||
import Test.Aeson.Utils (genValue, jsonOpenApiProperties, testToFromJSONToSchema)
|
||||
import Test.Hspec
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
describe "FunctionName" $ do
|
||||
testToFromJSONToSchema (TableName ["my_function_name"]) [aesonQQ|["my_function_name"]|]
|
||||
jsonOpenApiProperties genFunctionName
|
||||
|
||||
genFunctionName :: (MonadGen m) => m FunctionName
|
||||
genFunctionName = FunctionName <$> Gen.nonEmpty (linear 1 3) (genArbitraryAlphaNumText defaultRange)
|
||||
|
||||
genFunctionArgument :: Gen FunctionArgument
|
||||
genFunctionArgument =
|
||||
NamedArgument
|
||||
<$> genArbitraryAlphaNumText defaultRange
|
||||
<*> genArgumentValue
|
||||
|
||||
genArgumentValue :: Gen ArgumentValue
|
||||
genArgumentValue =
|
||||
fmap ScalarArgumentValue
|
||||
$ ScalarValue
|
||||
<$> genValue
|
||||
<*> genScalarType
|
@ -11,7 +11,7 @@ import Data.Aeson.QQ.Simple (aesonQQ)
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.CapabilitiesSpec (genUpdateColumnOperatorName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ColumnSpec (genColumnName, genColumnType, genColumnValueGenerationStrategy)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression, genTargetRedactionExpressions)
|
||||
import Hasura.Backends.DataConnector.API.V0.QuerySpec (genField, genFieldMap, genFieldValue)
|
||||
import Hasura.Backends.DataConnector.API.V0.RelationshipsSpec (genRelationshipName, genTableRelationships)
|
||||
import Hasura.Backends.DataConnector.API.V0.ScalarSpec (genScalarType)
|
||||
@ -28,7 +28,7 @@ spec :: Spec
|
||||
spec = do
|
||||
describe "MutationRequest" $ do
|
||||
testToFromJSONToSchema
|
||||
(MutationRequest [] [] [])
|
||||
(MutationRequest [] [] [] [])
|
||||
[aesonQQ|
|
||||
{ "table_relationships": [],
|
||||
"insert_schema": [],
|
||||
@ -93,7 +93,7 @@ spec = do
|
||||
jsonOpenApiProperties genInsertFieldSchema
|
||||
|
||||
describe "MutationOperation" $ do
|
||||
let returningFields = [(FieldName "field", ColumnField (ColumnName "my_column") (ScalarType "string"))]
|
||||
let returningFields = [(FieldName "field", ColumnField (ColumnName "my_column") (ScalarType "string") (Just $ RedactionExpressionName "RedactionExp1"))]
|
||||
describe "InsertOperation" $ do
|
||||
testToFromJSONToSchema
|
||||
(InsertOperation (InsertMutationOperation (TableName ["my_table"]) [] (Just $ And []) returningFields))
|
||||
@ -106,7 +106,8 @@ spec = do
|
||||
"field": {
|
||||
"type": "column",
|
||||
"column": "my_column",
|
||||
"column_type": "string"
|
||||
"column_type": "string",
|
||||
"redaction_expression": "RedactionExp1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -124,7 +125,8 @@ spec = do
|
||||
"field": {
|
||||
"type": "column",
|
||||
"column": "my_column",
|
||||
"column_type": "string"
|
||||
"column_type": "string",
|
||||
"redaction_expression": "RedactionExp1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -140,7 +142,8 @@ spec = do
|
||||
"field": {
|
||||
"type": "column",
|
||||
"column": "my_column",
|
||||
"column_type": "string"
|
||||
"column_type": "string",
|
||||
"redaction_expression": "RedactionExp1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,6 +253,7 @@ genMutationRequest :: Gen MutationRequest
|
||||
genMutationRequest =
|
||||
MutationRequest
|
||||
<$> Gen.set defaultRange genTableRelationships
|
||||
<*> Gen.set defaultRange genTargetRedactionExpressions
|
||||
<*> Gen.set defaultRange genTableInsertSchema
|
||||
<*> Gen.list defaultRange genMutationOperation
|
||||
|
||||
|
@ -13,7 +13,7 @@ import Data.HashMap.Strict qualified as HashMap
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.AggregateSpec (genSingleColumnAggregate)
|
||||
import Hasura.Backends.DataConnector.API.V0.ColumnSpec (genColumnName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression, genRedactionExpressionName)
|
||||
import Hasura.Backends.DataConnector.API.V0.RelationshipsSpec (genRelationshipName)
|
||||
import Hasura.Generator.Common (defaultRange)
|
||||
import Hasura.Prelude
|
||||
@ -28,10 +28,11 @@ spec = do
|
||||
describe "OrderByTarget" $ do
|
||||
describe "OrderByColumn"
|
||||
$ testToFromJSONToSchema
|
||||
(OrderByColumn (ColumnName "test_column"))
|
||||
(OrderByColumn (ColumnName "test_column") (Just $ RedactionExpressionName "RedactionExp2"))
|
||||
[aesonQQ|
|
||||
{ "type": "column",
|
||||
"column": "test_column"
|
||||
"column": "test_column",
|
||||
"redaction_expression": "RedactionExp2"
|
||||
}
|
||||
|]
|
||||
describe "OrderByStarCountAggregate"
|
||||
@ -42,12 +43,13 @@ spec = do
|
||||
|]
|
||||
describe "OrderBySingleColumnAggregate"
|
||||
$ testToFromJSONToSchema
|
||||
(OrderBySingleColumnAggregate (SingleColumnAggregate (SingleColumnAggregateFunction [G.name|sum|]) (ColumnName "test_column") (ScalarType "number")))
|
||||
(OrderBySingleColumnAggregate (SingleColumnAggregate (SingleColumnAggregateFunction [G.name|sum|]) (ColumnName "test_column") (Just $ RedactionExpressionName "RedactionExp2") (ScalarType "number")))
|
||||
[aesonQQ|
|
||||
{ "type": "single_column_aggregate",
|
||||
"function": "sum",
|
||||
"column": "test_column",
|
||||
"result_type": "number"
|
||||
"result_type": "number",
|
||||
"redaction_expression": "RedactionExp2"
|
||||
}
|
||||
|]
|
||||
jsonOpenApiProperties genOrderByTarget
|
||||
@ -56,14 +58,15 @@ spec = do
|
||||
testToFromJSONToSchema
|
||||
( OrderByElement
|
||||
[RelationshipName "relation1", RelationshipName "relation2"]
|
||||
(OrderByColumn (ColumnName "my_column_name"))
|
||||
(OrderByColumn (ColumnName "my_column_name") (Just $ RedactionExpressionName "RedactionExp2"))
|
||||
Ascending
|
||||
)
|
||||
[aesonQQ|
|
||||
{ "target_path": ["relation1", "relation2"],
|
||||
"target": {
|
||||
"type": "column",
|
||||
"column": "my_column_name"
|
||||
"column": "my_column_name",
|
||||
"redaction_expression": "RedactionExp2"
|
||||
},
|
||||
"order_direction": "asc"
|
||||
}
|
||||
@ -145,7 +148,7 @@ genOrderByElement =
|
||||
genOrderByTarget :: Gen OrderByTarget
|
||||
genOrderByTarget =
|
||||
Gen.choice
|
||||
[ OrderByColumn <$> genColumnName,
|
||||
[ OrderByColumn <$> genColumnName <*> Gen.maybe genRedactionExpressionName,
|
||||
pure OrderByStarCountAggregate,
|
||||
OrderBySingleColumnAggregate <$> genSingleColumnAggregate
|
||||
]
|
||||
|
@ -16,7 +16,8 @@ import Data.HashMap.Strict qualified as HashMap
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.AggregateSpec (genAggregate)
|
||||
import Hasura.Backends.DataConnector.API.V0.ColumnSpec (genColumnName)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression)
|
||||
import Hasura.Backends.DataConnector.API.V0.ExpressionSpec (genExpression, genRedactionExpressionName, genTargetRedactionExpressions)
|
||||
import Hasura.Backends.DataConnector.API.V0.FunctionSpec (genFunctionArgument, genFunctionName)
|
||||
import Hasura.Backends.DataConnector.API.V0.OrderBySpec (genOrderBy)
|
||||
import Hasura.Backends.DataConnector.API.V0.RelationshipsSpec (genRelationshipName, genRelationships)
|
||||
import Hasura.Backends.DataConnector.API.V0.ScalarSpec (genScalarType, genScalarValue)
|
||||
@ -25,7 +26,6 @@ import Hasura.Generator.Common (defaultRange, genArbitraryAlphaNumText, genHashM
|
||||
import Hasura.Prelude
|
||||
import Hedgehog
|
||||
import Hedgehog.Gen qualified as Gen
|
||||
import Hedgehog.Internal.Range (linear)
|
||||
import Test.Aeson.Utils (genValue, jsonOpenApiProperties, testToFromJSONToSchema)
|
||||
import Test.Hspec
|
||||
|
||||
@ -34,11 +34,12 @@ spec = do
|
||||
describe "Field" $ do
|
||||
describe "ColumnField"
|
||||
$ testToFromJSONToSchema
|
||||
(ColumnField (ColumnName "my_column_name") (ScalarType "string"))
|
||||
(ColumnField (ColumnName "my_column_name") (ScalarType "string") (Just $ RedactionExpressionName "RedactionExp0"))
|
||||
[aesonQQ|
|
||||
{ "type": "column",
|
||||
"column": "my_column_name",
|
||||
"column_type": "string"
|
||||
"column_type": "string",
|
||||
"redaction_expression": "RedactionExp0"
|
||||
}
|
||||
|]
|
||||
describe "RelationshipField" $ do
|
||||
@ -56,13 +57,13 @@ spec = do
|
||||
describe "Query" $ do
|
||||
let 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") Nothing)],
|
||||
_qAggregates = Just $ HashMap.fromList [(FieldName "my_aggregate", StarCount)],
|
||||
_qAggregatesLimit = Just 5,
|
||||
_qLimit = Just 10,
|
||||
_qOffset = Just 20,
|
||||
_qWhere = Just $ And [],
|
||||
_qOrderBy = Just $ OrderBy [] (OrderByElement [] (OrderByColumn (ColumnName "my_column_name")) Ascending :| [])
|
||||
_qOrderBy = Just $ OrderBy [] (OrderByElement [] (OrderByColumn (ColumnName "my_column_name") Nothing) Ascending :| [])
|
||||
}
|
||||
testToFromJSONToSchema
|
||||
query
|
||||
@ -95,6 +96,7 @@ spec = do
|
||||
$ TableRequest
|
||||
{ _trTable = TableName ["my_table"],
|
||||
_trRelationships = [],
|
||||
_trRedactionExpressions = [],
|
||||
_trQuery = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing Nothing,
|
||||
_trForeach = Just (HashMap.fromList [(ColumnName "my_id", ScalarValue (J.Number 666) (ScalarType "number"))] :| [])
|
||||
}
|
||||
@ -119,6 +121,7 @@ spec = do
|
||||
{ _frFunction = FunctionName ["my_function"],
|
||||
_frFunctionArguments = [],
|
||||
_frRelationships = [],
|
||||
_frRedactionExpressions = [],
|
||||
_frQuery = Query (Just mempty) Nothing Nothing Nothing Nothing Nothing Nothing
|
||||
}
|
||||
testToFromJSONToSchema
|
||||
@ -185,7 +188,7 @@ genField :: Gen Field
|
||||
genField =
|
||||
Gen.recursive
|
||||
Gen.choice
|
||||
[ColumnField <$> genColumnName <*> genScalarType]
|
||||
[ColumnField <$> genColumnName <*> genScalarType <*> Gen.maybe genRedactionExpressionName]
|
||||
[RelField <$> genRelationshipField]
|
||||
|
||||
genFieldName :: Gen FieldName
|
||||
@ -220,29 +223,15 @@ genFunctionRequest =
|
||||
<$> genFunctionName
|
||||
<*> Gen.list defaultRange genFunctionArgument
|
||||
<*> Gen.set defaultRange genRelationships
|
||||
<*> Gen.set defaultRange genTargetRedactionExpressions
|
||||
<*> genQuery
|
||||
|
||||
genFunctionName :: (MonadGen m) => m FunctionName
|
||||
genFunctionName = FunctionName <$> Gen.nonEmpty (linear 1 3) (genArbitraryAlphaNumText defaultRange)
|
||||
|
||||
genFunctionArgument :: Gen FunctionArgument
|
||||
genFunctionArgument =
|
||||
NamedArgument
|
||||
<$> genArbitraryAlphaNumText defaultRange
|
||||
<*> genArgumentValue
|
||||
|
||||
genArgumentValue :: Gen ArgumentValue
|
||||
genArgumentValue =
|
||||
fmap ScalarArgumentValue
|
||||
$ ScalarValue
|
||||
<$> genValue
|
||||
<*> genScalarType
|
||||
|
||||
genTableRequest :: Gen QueryRequest
|
||||
genTableRequest =
|
||||
TableQueryRequest
|
||||
<$> genTableName
|
||||
<*> Gen.set defaultRange genRelationships
|
||||
<*> Gen.set defaultRange genTargetRedactionExpressions
|
||||
<*> genQuery
|
||||
<*> Gen.maybe (Gen.nonEmpty defaultRange (genHashMap genColumnName genScalarValue defaultRange))
|
||||
|
||||
|
@ -0,0 +1,47 @@
|
||||
{-# LANGUAGE OverloadedLists #-}
|
||||
{-# LANGUAGE QuasiQuotes #-}
|
||||
|
||||
module Hasura.Backends.DataConnector.API.V0.TargetSpec
|
||||
( spec,
|
||||
genTargetName,
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Aeson.QQ.Simple (aesonQQ)
|
||||
import Hasura.Backends.DataConnector.API.V0
|
||||
import Hasura.Backends.DataConnector.API.V0.FunctionSpec (genFunctionName)
|
||||
import Hasura.Backends.DataConnector.API.V0.TableSpec (genTableName)
|
||||
import Hasura.Prelude
|
||||
import Hedgehog
|
||||
import Hedgehog.Gen qualified as Gen
|
||||
import Test.Aeson.Utils (jsonOpenApiProperties, testToFromJSONToSchema)
|
||||
import Test.Hspec
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
describe "TargetName" $ do
|
||||
describe "TNTable"
|
||||
$ testToFromJSONToSchema
|
||||
(TNTable $ TableName ["my_table"])
|
||||
[aesonQQ|
|
||||
{ "type": "table",
|
||||
"table": ["my_table"]
|
||||
}
|
||||
|]
|
||||
describe "TNFunction"
|
||||
$ testToFromJSONToSchema
|
||||
(TNFunction $ FunctionName ["my_function"])
|
||||
[aesonQQ|
|
||||
{ "type": "function",
|
||||
"function": ["my_function"]
|
||||
}
|
||||
|]
|
||||
|
||||
jsonOpenApiProperties genTargetName
|
||||
|
||||
genTargetName :: (MonadGen m) => m TargetName
|
||||
genTargetName =
|
||||
Gen.choice
|
||||
[ TNTable <$> genTableName,
|
||||
TNFunction <$> genFunctionName
|
||||
]
|
Loading…
Reference in New Issue
Block a user