Use JSON instead of GraphQL for comparison operators

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6563
GitOrigin-RevId: 0819414df199292e0146ce3009295ce3e49f2439
This commit is contained in:
David Overton 2022-10-28 12:12:54 +11:00 committed by hasura-bot
parent 72dbafc319
commit 87bcdb97c7
22 changed files with 121 additions and 262 deletions

View File

@ -133,9 +133,8 @@ The `GET /capabilities` endpoint is used by `graphql-engine` to discover the cap
"column_nullability": "nullable_and_non_nullable"
},
"relationships": {},
"graphql_schema": "scalar DateTime\n\ninput DateTimeComparisons {\n in_year: Number\n}",
"scalar_types": {
"DateTime": {"comparisonType": "DateTimeComparisons"}
"DateTime": {"comparison_operators": {"DateTime": {"in_year": "Number"}}}
}
},
"config_schemas": {
@ -166,7 +165,6 @@ The `capabilities` section describes the _capabilities_ of the service. This inc
- `data_schema`: What sorts of features the agent supports when describing its data schema
- `relationships`: whether or not the agent supports relationships
- `scalar_types`: custom scalar types and the operations they support. See [Scalar types capabilities](#scalar-type-capabilities).
- `graphql_schema`: a GraphQL schema document containing type definitions referenced by the `scalar_types` capabilities.
The `config_schema` property contains an [OpenAPI 3 Schema](https://swagger.io/specification/#schema-object) object that represents the schema of the configuration object. It can use references (`$ref`) to refer to other schemas defined in the `other_schemas` object by name.
@ -180,7 +178,7 @@ If the agent only supports table columns that are always nullable, then it shoul
#### Scalar type capabilities
The agent is expected to support a default set of scalar types (`Number`, `String`, `Bool`) and a default set of [comparison operators](#filters) on these types.
Agents may optionally declare support for their own custom scalar types and custom comparison operators on those types.
Agents may optionally declare support for their own custom scalar types, along with custom comparison operators and aggregate functions on those types.
Hasura GraphQL Engine does not validate the JSON format for values of custom scalar types.
It passes them through transparently to the agent when they are used as GraphQL input values and returns them transparently when they are produced by the agent.
It is the agent's responsibility to validate the values provided as GraphQL inputs.
@ -188,33 +186,31 @@ It is the agent's responsibility to validate the values provided as GraphQL inpu
Custom scalar types are declared by adding a property to the `scalar_types` section of the [capabilities](#capabilities-and-configuration-schema) and
by adding scalar type declaration with the same name in the `graphql_schema` capabilities property.
Custom comparison types can be defined by adding a `comparisonType` property to the scalar type capabilities object.
The `comparisonType` property gives the name of a GraphQL input object type, which must be defined in the `graphql_schema` capabilities property.
The input object type will be spliced into the `where` argument for any columns of the scalar type in the GraphQL schema.
Custom comparison types can be defined by adding a `comparison_operators` property to the scalar type capabilities object.
The `comparison_operators` property is an object where each key specifies a comparison operator name.
The operator name must be a valid GraphQL name.
The value associated with each key should be a string specifying the argument type, which must be a valid scalar type.
Custom aggregate functions can be defined by adding a `aggregate_functions` property to the scalar type capabilities object.
Custom aggregate functions can be defined by adding an `aggregate_functions` property to the scalar type capabilities object.
The `aggregate_functions` property must be an object mapping aggregate function names to their result types.
Aggregate function names must be must be valid GraphQL names.
Result types must be valid scalar types.
Example:
```yaml
capabilities:
graphql_schema: |
scalar DateTime
input DateTimeComparisons {
in_year: Number
}
scalar_types:
DateTime:
comparisonType: DateTimeComparisons
comparison_operators:
in_year: Number
aggregate_functions:
max: 'DateTime'
min: 'DateTime'
```
This example declares a custom scalar type `DateTime`, with comparison operators defined by the GraphQL input object type `DateTimeComparisons`.
The input type `DateTimeComparisons` defines one comparison operator `in_year` which takes a `Number` argument
This example declares a custom scalar type `DateTime`.
The type supports a comparison operator `in_year`, which takes an argument of type `Number`.
An example GraphQL query using the custom comparison operator might look like below:
```graphql

View File

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

View File

@ -312,9 +312,6 @@
"explain": {
"$ref": "#/components/schemas/ExplainCapabilities"
},
"graphql_schema": {
"$ref": "#/components/schemas/GraphQLTypeDefinitions"
},
"metrics": {
"$ref": "#/components/schemas/MetricsCapabilities"
},
@ -367,10 +364,6 @@
"QueryCapabilities": {},
"MutationCapabilities": {},
"SubscriptionCapabilities": {},
"GraphQLName": {
"description": "A valid GraphQL name",
"type": "string"
},
"ScalarType": {
"additionalProperties": true,
"anyOf": [
@ -387,6 +380,13 @@
}
]
},
"ComparisonOperators": {
"additionalProperties": {
"$ref": "#/components/schemas/ScalarType"
},
"description": "A map from comparison operator names to their argument types.\nOperator and argument type names must be valid GraphQL names.\nResult type names must be defined scalar types - either builtin or declared in ScalarTypesCapabilities.\n",
"type": "object"
},
"AggregateFunctions": {
"additionalProperties": {
"$ref": "#/components/schemas/ScalarType"
@ -395,13 +395,13 @@
"type": "object"
},
"ScalarTypeCapabilities": {
"description": "Capabilities of a scalar type.\ncomparison_type: Name of the GraphQL input object to be used for comparison operations on the scalar type. The input object type must be defined in the `graphql_schema`.\naggregate_functions: The aggregate functions supported by the scalar type.\n",
"description": "Capabilities of a scalar type.\ncomparison_operators: The comparison operators supported by the scalar type.\naggregate_functions: The aggregate functions supported by the scalar type.\n",
"properties": {
"aggregate_functions": {
"$ref": "#/components/schemas/AggregateFunctions"
},
"comparison_type": {
"$ref": "#/components/schemas/GraphQLName"
"comparison_operators": {
"$ref": "#/components/schemas/ComparisonOperators"
}
},
"type": "object"
@ -413,10 +413,6 @@
"description": "A map from scalar type names to their capabilities.\nKeys must be valid GraphQL names and must be defined as scalar types in the `graphql_schema`\n",
"type": "object"
},
"GraphQLTypeDefinitions": {
"description": "A valid GraphQL schema document containing type definitions",
"type": "string"
},
"RelationshipCapabilities": {},
"SubqueryComparisonCapabilities": {
"nullable": true,

View File

@ -20,6 +20,7 @@ export type { ColumnInfo } from './models/ColumnInfo';
export type { ColumnNullability } from './models/ColumnNullability';
export type { ComparisonCapabilities } from './models/ComparisonCapabilities';
export type { ComparisonColumn } from './models/ComparisonColumn';
export type { ComparisonOperators } from './models/ComparisonOperators';
export type { ComparisonValue } from './models/ComparisonValue';
export type { ConfigSchemaResponse } from './models/ConfigSchemaResponse';
export type { Constraint } from './models/Constraint';
@ -32,8 +33,6 @@ export type { ExplainCapabilities } from './models/ExplainCapabilities';
export type { ExplainResponse } from './models/ExplainResponse';
export type { Expression } from './models/Expression';
export type { Field } from './models/Field';
export type { GraphQLName } from './models/GraphQLName';
export type { GraphQLTypeDefinitions } from './models/GraphQLTypeDefinitions';
export type { MetricsCapabilities } from './models/MetricsCapabilities';
export type { MutationCapabilities } from './models/MutationCapabilities';
export type { NotExpression } from './models/NotExpression';

View File

@ -5,7 +5,6 @@
import type { ComparisonCapabilities } from './ComparisonCapabilities';
import type { DataSchemaCapabilities } from './DataSchemaCapabilities';
import type { ExplainCapabilities } from './ExplainCapabilities';
import type { GraphQLTypeDefinitions } from './GraphQLTypeDefinitions';
import type { MetricsCapabilities } from './MetricsCapabilities';
import type { MutationCapabilities } from './MutationCapabilities';
import type { QueryCapabilities } from './QueryCapabilities';
@ -18,7 +17,6 @@ export type Capabilities = {
comparisons?: ComparisonCapabilities;
data_schema?: DataSchemaCapabilities;
explain?: ExplainCapabilities;
graphql_schema?: GraphQLTypeDefinitions;
metrics?: MetricsCapabilities;
mutations?: MutationCapabilities;
queries?: QueryCapabilities;

View File

@ -0,0 +1,13 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { ScalarType } from './ScalarType';
/**
* A map from comparison operator names to their argument types.
* Operator and argument type names must be valid GraphQL names.
* Result type names must be defined scalar types - either builtin or declared in ScalarTypesCapabilities.
*
*/
export type ComparisonOperators = Record<string, ScalarType>;

View File

@ -1,8 +0,0 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/**
* A valid GraphQL name
*/
export type GraphQLName = string;

View File

@ -1,8 +0,0 @@
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
/**
* A valid GraphQL schema document containing type definitions
*/
export type GraphQLTypeDefinitions = string;

View File

@ -3,16 +3,16 @@
/* eslint-disable */
import type { AggregateFunctions } from './AggregateFunctions';
import type { GraphQLName } from './GraphQLName';
import type { ComparisonOperators } from './ComparisonOperators';
/**
* Capabilities of a scalar type.
* comparison_type: Name of the GraphQL input object to be used for comparison operations on the scalar type. The input object type must be defined in the `graphql_schema`.
* comparison_operators: The comparison operators supported by the scalar type.
* aggregate_functions: The aggregate functions supported by the scalar type.
*
*/
export type ScalarTypeCapabilities = {
aggregate_functions?: AggregateFunctions;
comparison_type?: GraphQLName;
comparison_operators?: ComparisonOperators;
};

View File

@ -24,7 +24,7 @@
},
"dc-api-types": {
"name": "@hasura/dc-api-types",
"version": "0.13.0",
"version": "0.14.0",
"license": "Apache-2.0",
"devDependencies": {
"@tsconfig/node16": "^1.0.3",
@ -631,7 +631,7 @@
"license": "Apache-2.0",
"dependencies": {
"@fastify/cors": "^7.0.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify": "^3.29.0",
"mathjs": "^11.0.0",
"pino-pretty": "^8.0.0",
@ -1389,7 +1389,7 @@
"license": "Apache-2.0",
"dependencies": {
"@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify": "^4.4.0",
"fastify-metrics": "^9.2.1",
"nanoid": "^3.3.4",
@ -3122,7 +3122,7 @@
"version": "file:reference",
"requires": {
"@fastify/cors": "^7.0.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"@tsconfig/node16": "^1.0.3",
"@types/node": "^16.11.49",
"@types/xml2js": "^0.4.11",
@ -3613,7 +3613,7 @@
"version": "file:sqlite",
"requires": {
"@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"@tsconfig/node16": "^1.0.3",
"@types/node": "^16.11.49",
"@types/sqlite3": "^3.1.8",

View File

@ -10,7 +10,7 @@
"license": "Apache-2.0",
"dependencies": {
"@fastify/cors": "^7.0.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify": "^3.29.0",
"mathjs": "^11.0.0",
"pino-pretty": "^8.0.0",
@ -44,7 +44,7 @@
}
},
"node_modules/@hasura/dc-api-types": {
"version": "0.13.0",
"version": "0.14.0",
"license": "Apache-2.0",
"devDependencies": {
"@tsconfig/node16": "^1.0.3",

View File

@ -22,7 +22,7 @@
},
"dependencies": {
"@fastify/cors": "^7.0.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify": "^3.29.0",
"mathjs": "^11.0.0",
"pino-pretty": "^8.0.0",

View File

@ -1,17 +1,11 @@
import { configSchema } from "./config"
import { Capabilities, CapabilitiesResponse, ScalarTypeCapabilities, ScalarTypesCapabilities } from "@hasura/dc-api-types"
const schemaDoc: string =
`scalar DateTime
input DateTimeComparisons {
same_day_as: DateTime
in_year: Int
}
`
const dateTimeCapabilities: ScalarTypeCapabilities = {
comparison_type: 'DateTimeComparisons',
comparison_operators: {
same_day_as: 'DateTime',
in_year: 'Int'
},
aggregate_functions: {
max: 'DateTime',
min: 'DateTime'
@ -43,7 +37,6 @@ const capabilities: Capabilities = {
supports_relations: true
}
},
graphql_schema: schemaDoc,
scalar_types: scalarTypes
}

View File

@ -10,7 +10,7 @@
"license": "Apache-2.0",
"dependencies": {
"@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify": "^4.4.0",
"fastify-metrics": "^9.2.1",
"nanoid": "^3.3.4",
@ -54,7 +54,7 @@
"license": "MIT"
},
"node_modules/@hasura/dc-api-types": {
"version": "0.13.0",
"version": "0.14.0",
"license": "Apache-2.0",
"devDependencies": {
"@tsconfig/node16": "^1.0.3",

View File

@ -22,7 +22,7 @@
},
"dependencies": {
"@fastify/cors": "^8.1.0",
"@hasura/dc-api-types": "0.13.0",
"@hasura/dc-api-types": "0.14.0",
"fastify-metrics": "^9.2.1",
"fastify": "^4.4.0",
"nanoid": "^3.3.4",

View File

@ -10,7 +10,6 @@ export const capabilitiesResponse: CapabilitiesResponse = {
supports_foreign_keys: true,
column_nullability: "nullable_and_non_nullable",
},
graphql_schema: "scalar DateTime",
scalar_types: {
DateTime: {}
},

View File

@ -13,10 +13,10 @@ module Hasura.Backends.DataConnector.API.V0.Capabilities
QueryCapabilities (..),
MutationCapabilities (..),
SubscriptionCapabilities (..),
ComparisonOperators (..),
AggregateFunctions (..),
ScalarTypeCapabilities (..),
ScalarTypesCapabilities (..),
GraphQLTypeDefinitions,
RelationshipCapabilities (..),
ComparisonCapabilities (..),
SubqueryComparisonCapabilities (..),
@ -24,8 +24,6 @@ module Hasura.Backends.DataConnector.API.V0.Capabilities
ExplainCapabilities (..),
RawCapabilities (..),
CapabilitiesResponse (..),
lookupComparisonInputObjectDefinition,
mkGraphQLTypeDefinitions,
)
where
@ -70,8 +68,7 @@ data Capabilities = Capabilities
_cQueries :: Maybe QueryCapabilities,
_cMutations :: Maybe MutationCapabilities,
_cSubscriptions :: Maybe SubscriptionCapabilities,
_cScalarTypes :: Maybe ScalarTypesCapabilities,
_cGraphQLTypeDefinitions :: Maybe GraphQLTypeDefinitions,
_cScalarTypes :: ScalarTypesCapabilities,
_cRelationships :: Maybe RelationshipCapabilities,
_cComparisons :: Maybe ComparisonCapabilities,
_cMetrics :: Maybe MetricsCapabilities,
@ -83,7 +80,7 @@ data Capabilities = Capabilities
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec Capabilities
defaultCapabilities :: Capabilities
defaultCapabilities = Capabilities defaultDataSchemaCapabilities Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
defaultCapabilities = Capabilities defaultDataSchemaCapabilities Nothing Nothing Nothing mempty Nothing Nothing Nothing Nothing Nothing
instance HasCodec Capabilities where
codec =
@ -93,8 +90,7 @@ instance HasCodec Capabilities where
<*> optionalField "queries" "The agent's query capabilities" .= _cQueries
<*> optionalField "mutations" "The agent's mutation capabilities" .= _cMutations
<*> optionalField "subscriptions" "The agent's subscription capabilities" .= _cSubscriptions
<*> optionalField "scalar_types" "The agent's scalar types and their capabilities" .= _cScalarTypes
<*> optionalField "graphql_schema" "A GraphQL Schema Document describing the agent's scalar types and input object types for comparison operators" .= _cGraphQLTypeDefinitions
<*> optionalFieldWithOmittedDefault "scalar_types" mempty "The agent's scalar types and their capabilities" .= _cScalarTypes
<*> optionalField "relationships" "The agent's relationship capabilities" .= _cRelationships
<*> optionalField "comparisons" "The agent's comparison capabilities" .= _cComparisons
<*> optionalField "metrics" "The agent's metrics capabilities" .= _cMetrics
@ -170,11 +166,29 @@ data RelationshipCapabilities = RelationshipCapabilities {}
instance HasCodec RelationshipCapabilities where
codec = object "RelationshipCapabilities" $ pure RelationshipCapabilities
newtype ComparisonOperators = ComparisonOperators
{ unComparisonOperators :: HashMap GQL.Syntax.Name ScalarType
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (NFData, Hashable)
deriving newtype (Semigroup, Monoid)
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec ComparisonOperators
instance HasCodec ComparisonOperators where
codec =
named "ComparisonOperators" $
dimapCodec ComparisonOperators unComparisonOperators (hashMapCodec codec)
<??> [ "A map from comparison operator names to their argument types.",
"Operator and argument type names must be valid GraphQL names.",
"Result type names must be defined scalar types - either builtin or declared in ScalarTypesCapabilities."
]
newtype AggregateFunctions = AggregateFunctions
{ unAggregateFunctions :: HashMap GQL.Syntax.Name ScalarType
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (NFData, Hashable)
deriving newtype (Semigroup, Monoid)
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec AggregateFunctions
instance HasCodec AggregateFunctions where
@ -187,8 +201,8 @@ instance HasCodec AggregateFunctions where
]
data ScalarTypeCapabilities = ScalarTypeCapabilities
{ _stcComparisonInputObject :: Maybe GQL.Syntax.Name,
_stcAggregateFunctions :: Maybe AggregateFunctions
{ _stcComparisonOperators :: ComparisonOperators,
_stcAggregateFunctions :: AggregateFunctions
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (NFData, Hashable)
@ -199,11 +213,11 @@ instance HasCodec ScalarTypeCapabilities where
object
"ScalarTypeCapabilities"
( ScalarTypeCapabilities
<$> optionalFieldWith' "comparison_type" nameCodec .= _stcComparisonInputObject
<*> optionalField' "aggregate_functions" .= _stcAggregateFunctions
<$> optionalFieldWithOmittedDefault' "comparison_operators" mempty .= _stcComparisonOperators
<*> optionalFieldWithOmittedDefault' "aggregate_functions" mempty .= _stcAggregateFunctions
)
<??> [ "Capabilities of a scalar type.",
"comparison_type: Name of the GraphQL input object to be used for comparison operations on the scalar type. The input object type must be defined in the `graphql_schema`.",
"comparison_operators: The comparison operators supported by the scalar type.",
"aggregate_functions: The aggregate functions supported by the scalar type."
]
@ -223,63 +237,6 @@ instance HasCodec ScalarTypesCapabilities where
"Keys must be valid GraphQL names and must be defined as scalar types in the `graphql_schema`"
]
type TypeDefinition = GQL.Syntax.TypeDefinition () GQL.Syntax.InputValueDefinition
mkGraphQLTypeDefinitions :: NonEmpty TypeDefinition -> GraphQLTypeDefinitions
mkGraphQLTypeDefinitions =
GraphQLTypeDefinitions
. InsOrdHashMap.fromList
. toList
. fmap (\td -> (getName td, td))
where
getName :: TypeDefinition -> GQL.Syntax.Name
getName = \case
GQL.Syntax.TypeDefinitionScalar GQL.Syntax.ScalarTypeDefinition {..} -> _stdName
GQL.Syntax.TypeDefinitionObject GQL.Syntax.ObjectTypeDefinition {..} -> _otdName
GQL.Syntax.TypeDefinitionInterface GQL.Syntax.InterfaceTypeDefinition {..} -> _itdName
GQL.Syntax.TypeDefinitionUnion GQL.Syntax.UnionTypeDefinition {..} -> _utdName
GQL.Syntax.TypeDefinitionEnum GQL.Syntax.EnumTypeDefinition {..} -> _etdName
GQL.Syntax.TypeDefinitionInputObject GQL.Syntax.InputObjectTypeDefinition {..} -> _iotdName
newtype GraphQLTypeDefinitions = GraphQLTypeDefinitions
{ _gtdTypeDefinitions :: InsOrdHashMap GQL.Syntax.Name TypeDefinition
}
deriving stock (Eq, Show, Generic)
deriving anyclass (NFData, Hashable)
deriving (FromJSON, ToJSON, ToSchema) via Autodocodec GraphQLTypeDefinitions
instance HasCodec GraphQLTypeDefinitions where
codec =
bimapCodec parseTypeDefinitions printTypeDefinitions (StringCodec (Just "GraphQLTypeDefinitions"))
<?> "A valid GraphQL schema document containing type definitions"
where
-- Note: any `SchemaDefinition`s in the parsed `SchemaDocument` will be ignored.
-- We don't need them, we're only interested in the `TypeDefinition`s defined in the document.
getTypeDefinition :: GQL.Syntax.TypeSystemDefinition -> Maybe TypeDefinition
getTypeDefinition = \case
GQL.Syntax.TypeSystemDefinitionSchema _ -> Nothing
GQL.Syntax.TypeSystemDefinitionType td -> Just td
fromSchemaDocument :: GQL.Syntax.SchemaDocument -> Either String GraphQLTypeDefinitions
fromSchemaDocument (GQL.Syntax.SchemaDocument typeSystemDefinitions) =
case nonEmpty $ mapMaybe getTypeDefinition typeSystemDefinitions of
Nothing -> Left "No type definitions found in schema document"
Just typeDefinitions -> Right $ mkGraphQLTypeDefinitions typeDefinitions
parseTypeDefinitions :: Text -> Either String GraphQLTypeDefinitions
parseTypeDefinitions =
fromSchemaDocument <=< first Text.unpack . GQL.Parser.parseSchemaDocument
printTypeDefinitions :: GraphQLTypeDefinitions -> Text
printTypeDefinitions =
toStrict
. Builder.toLazyText
. GQL.Printer.schemaDocument
. GQL.Syntax.SchemaDocument
. fmap GQL.Syntax.TypeSystemDefinitionType
. toList
. _gtdTypeDefinitions
data ComparisonCapabilities = ComparisonCapabilities
{_ccSubqueryComparisonCapabilities :: Maybe SubqueryComparisonCapabilities}
deriving stock (Eq, Ord, Show, Generic, Data)
@ -365,14 +322,3 @@ instance ToSchema CapabilitiesResponse where
}
pure $ NamedSchema (Just "CapabilitiesResponse") schema
lookupComparisonInputObjectDefinition :: Capabilities -> ScalarType -> Maybe (GQL.Syntax.InputObjectTypeDefinition GQL.Syntax.InputValueDefinition)
lookupComparisonInputObjectDefinition Capabilities {..} typeName = do
scalarTypesMap <- _cScalarTypes
ScalarTypeCapabilities {..} <- HashMap.lookup typeName $ unScalarTypesCapabilities scalarTypesMap
comparisonTypeName <- _stcComparisonInputObject
typeDefinitions <- _cGraphQLTypeDefinitions
typeDefinition <- InsOrdHashMap.lookup comparisonTypeName $ _gtdTypeDefinitions typeDefinitions
case typeDefinition of
GQL.Syntax.TypeDefinitionInputObject inputObjectTypeDefinition -> Just inputObjectTypeDefinition
_ -> Nothing

View File

@ -42,8 +42,7 @@ capabilities =
API._cQueries = Just API.QueryCapabilities,
API._cMutations = Nothing,
API._cSubscriptions = Nothing,
API._cScalarTypes = Nothing,
API._cGraphQLTypeDefinitions = Nothing,
API._cScalarTypes = mempty,
API._cRelationships = Just API.RelationshipCapabilities {},
API._cComparisons =
Just

View File

@ -56,8 +56,6 @@ defaultBackendCapabilities = \case
supports_foreign_keys: true
scalar_types:
DateTime: {}
graphql_schema: |-
scalar DateTime
queries: {}
relationships: {}
comparisons:
@ -74,19 +72,15 @@ defaultBackendCapabilities = \case
supports_primary_keys: true
supports_foreign_keys: true
queries: {}
graphql_schema: |-
scalar DateTime
input DateTimeComparisons {in_year: Int
same_day_as: DateTime
}
relationships: {}
comparisons:
subquery:
supports_relations: true
scalar_types:
DateTime:
comparison_type: DateTimeComparisons
comparison_operators:
same_day_as: DateTime
in_year: Int
aggregate_functions:
max: DateTime
min: DateTime

View File

@ -92,10 +92,10 @@ instance Backend 'DataConnector where
getCustomAggregateOperators Adapter.SourceConfig {..} =
HashMap.foldrWithKey insertOps mempty scalarTypesCapabilities
where
scalarTypesCapabilities = maybe mempty API.unScalarTypesCapabilities $ API._cScalarTypes _scCapabilities
scalarTypesCapabilities = API.unScalarTypesCapabilities $ API._cScalarTypes _scCapabilities
insertOps typeName API.ScalarTypeCapabilities {..} m =
HashMap.foldrWithKey insertOp m $
maybe mempty API.unAggregateFunctions _stcAggregateFunctions
API.unAggregateFunctions _stcAggregateFunctions
where
insertOp funtionName resultTypeName =
HashMap.insertWith HashMap.union funtionName $

View File

@ -12,7 +12,7 @@ import Data.HashMap.Strict qualified as Map
import Data.List.NonEmpty qualified as NE
import Data.Text.Casing (GQLNameIdentifier, fromCustomName)
import Data.Text.Extended ((<<>))
import Hasura.Backends.DataConnector.API.V0.Capabilities (lookupComparisonInputObjectDefinition)
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Backends.DataConnector.Adapter.Backend (CustomBooleanOperator (..), columnTypeToScalarType)
import Hasura.Backends.DataConnector.Adapter.Types qualified as DC
import Hasura.Base.Error
@ -204,28 +204,28 @@ comparisonExps' sourceInfo columnType = P.memoizeOn 'comparisonExps' (dataConnec
GS.C.SchemaT r m [P.InputFieldsParser n (Maybe (CustomBooleanOperator (IR.UnpreparedValue 'DataConnector)))]
mkCustomOperators tCase collapseIfNull typeName = do
let capabilities = sourceInfo ^. RQL.siConfiguration . DC.scCapabilities
case lookupComparisonInputObjectDefinition capabilities (Witch.from $ DC.fromGQLType typeName) of
case Map.lookup (Witch.from $ DC.fromGQLType typeName) (API.unScalarTypesCapabilities $ API._cScalarTypes capabilities) of
Nothing -> pure []
Just GQL.InputObjectTypeDefinition {..} -> do
traverse (mkCustomOperator tCase collapseIfNull) _iotdValueDefinitions
Just API.ScalarTypeCapabilities {..} -> do
traverse (mkCustomOperator tCase collapseIfNull) $ Map.toList $ fmap Witch.from $ API.unComparisonOperators $ _stcComparisonOperators
mkCustomOperator ::
NamingCase ->
Options.DangerouslyCollapseBooleans ->
GQL.InputValueDefinition ->
(GQL.Name, DC.ScalarType) ->
GS.C.SchemaT r m (P.InputFieldsParser n (Maybe (CustomBooleanOperator (IR.UnpreparedValue 'DataConnector))))
mkCustomOperator tCase collapseIfNull GQL.InputValueDefinition {..} = do
argParser <- mkArgParser _ivdType
mkCustomOperator tCase collapseIfNull (operatorName, argType) = do
argParser <- mkArgParser argType
pure $
GS.BE.mkBoolOperator tCase collapseIfNull (fromCustomName _ivdName) _ivdDescription $
CustomBooleanOperator (GQL.unName _ivdName) . Just . Right <$> argParser
GS.BE.mkBoolOperator tCase collapseIfNull (fromCustomName operatorName) Nothing $
CustomBooleanOperator (GQL.unName operatorName) . Just . Right <$> argParser
mkArgParser :: GQL.GType -> GS.C.SchemaT r m (P.Parser 'P.Both n (IR.UnpreparedValue 'DataConnector))
mkArgParser :: DC.ScalarType -> GS.C.SchemaT r m (P.Parser 'P.Both n (IR.UnpreparedValue 'DataConnector))
mkArgParser argType =
fmap IR.mkParameter
<$> columnParser'
(RQL.ColumnScalar $ DC.fromGQLType $ GQL.getBaseType argType)
(GQL.Nullability $ GQL.isNotNull argType)
(RQL.ColumnScalar argType)
(GQL.Nullability True)
tableArgs' ::
forall r m n.

View File

@ -3,10 +3,8 @@
module Hasura.Backends.DataConnector.API.V0.CapabilitiesSpec (spec) where
import Data.Aeson (Value (..))
import Data.Aeson.QQ.Simple (aesonQQ)
import Data.HashMap.Strict qualified as HashMap
import Data.Text.RawString (raw)
import Hasura.Backends.DataConnector.API.V0.Capabilities
import Hasura.Backends.DataConnector.API.V0.ConfigSchema
import Hasura.Backends.DataConnector.API.V0.Scalar (ScalarType (..))
@ -15,8 +13,6 @@ import Hasura.Generator.Common
import Hasura.Prelude
import Hedgehog
import Hedgehog.Gen qualified as Gen
import Language.GraphQL.Draft.Generator (genTypeDefinition)
import Language.GraphQL.Draft.Syntax qualified as G
import Language.GraphQL.Draft.Syntax.QQ qualified as G
import Test.Aeson.Utils
import Test.Hspec
@ -31,36 +27,21 @@ spec = do
(CapabilitiesResponse (defaultCapabilities {_cRelationships = Just RelationshipCapabilities {}}) emptyConfigSchemaResponse)
[aesonQQ|{"capabilities": {"relationships": {}}, "config_schemas": {"config_schema": {}, "other_schemas": {}}}|]
describe "ScalarTypesCapabilities" $ do
testToFromJSONToSchema (ScalarTypesCapabilities (HashMap.singleton StringTy (ScalarTypeCapabilities Nothing Nothing))) [aesonQQ|{"string": {}}|]
testToFromJSONToSchema (ScalarTypesCapabilities (HashMap.singleton StringTy (ScalarTypeCapabilities mempty mempty))) [aesonQQ|{"string": {}}|]
jsonOpenApiProperties genScalarTypesCapabilities
describe "ScalarTypeCapabilities" $ do
testToFromJSONToSchema (ScalarTypeCapabilities (Just [G.name|DateTimeComparisons|]) Nothing) [aesonQQ|{"comparison_type": "DateTimeComparisons"}|]
describe "GraphQLTypeDefinitions" $ do
testToFromJSONToSchema sampleGraphQLTypeDefinitions sampleGraphQLTypeDefinitionsJSON
sampleGraphQLTypeDefinitions :: GraphQLTypeDefinitions
sampleGraphQLTypeDefinitions =
mkGraphQLTypeDefinitions
[ G.TypeDefinitionScalar $ G.ScalarTypeDefinition Nothing [G.name|DateTime|] [],
G.TypeDefinitionInputObject $
G.InputObjectTypeDefinition
Nothing
[G.name|DateTimeComparisons|]
[]
[ G.InputValueDefinition Nothing [G.name|after|] (G.TypeNamed (G.Nullability True) [G.name|DateTime|]) Nothing [],
G.InputValueDefinition Nothing [G.name|before|] (G.TypeNamed (G.Nullability True) [G.name|DateTime|]) Nothing [],
G.InputValueDefinition Nothing [G.name|in_year|] (G.TypeNamed (G.Nullability True) [G.name|Int|]) Nothing []
]
]
sampleGraphQLTypeDefinitionsJSON :: Value
sampleGraphQLTypeDefinitionsJSON =
[raw|scalar DateTime
input DateTimeComparisons {after: DateTime
before: DateTime
in_year: Int
}|]
let comparisonOperators = ComparisonOperators $ HashMap.fromList [([G.name|same_day_as|], CustomTy "DateTime")]
let aggregateFunctions = AggregateFunctions $ HashMap.fromList [([G.name|max|], CustomTy "DateTime")]
let json =
[aesonQQ|{
"comparison_operators": {
"same_day_as": "DateTime"
},
"aggregate_functions": {
"max": "DateTime"
}
}|]
testToFromJSONToSchema (ScalarTypeCapabilities comparisonOperators aggregateFunctions) json
genDataSchemaCapabilities :: MonadGen m => m DataSchemaCapabilities
genDataSchemaCapabilities =
@ -82,6 +63,10 @@ genMutationCapabilities = pure MutationCapabilities {}
genSubscriptionCapabilities :: MonadGen m => m SubscriptionCapabilities
genSubscriptionCapabilities = pure SubscriptionCapabilities {}
genComparisonOperators :: MonadGen m => m ComparisonOperators
genComparisonOperators =
ComparisonOperators <$> genHashMap (genGName defaultRange) genScalarType defaultRange
genAggregateFunctions :: MonadGen m => m AggregateFunctions
genAggregateFunctions =
AggregateFunctions <$> genHashMap (genGName defaultRange) genScalarType defaultRange
@ -89,55 +74,13 @@ genAggregateFunctions =
genScalarTypeCapabilities :: MonadGen m => m ScalarTypeCapabilities
genScalarTypeCapabilities =
ScalarTypeCapabilities
<$> Gen.maybe (genGName defaultRange)
<*> Gen.maybe genAggregateFunctions
<$> genComparisonOperators
<*> genAggregateFunctions
genScalarTypesCapabilities :: MonadGen m => m ScalarTypesCapabilities
genScalarTypesCapabilities =
ScalarTypesCapabilities <$> genHashMap genScalarType genScalarTypeCapabilities defaultRange
-- | 'genTypeDefinition' generates invalid type definitions so we need to filter them out.
-- The printers also sort various lists upon printing, so we need to pre-sort them for round-tripping to work.
-- The printer for 'ObjectTypeDefinition' prints directives in the wrong place so we only allow
-- definitions with no directives.
-- TODO: fix this in `graphql-parser-hs`.
isValidTypeDefinition :: Ord inputType => G.TypeDefinition possibleTypes inputType -> Maybe (G.TypeDefinition possibleTypes inputType)
isValidTypeDefinition = \case
t@(G.TypeDefinitionScalar G.ScalarTypeDefinition {}) -> Just t
G.TypeDefinitionObject G.ObjectTypeDefinition {..} -> do
guard $ not $ null _otdFieldsDefinition
Just $
G.TypeDefinitionObject
G.ObjectTypeDefinition
{ _otdFieldsDefinition = sort _otdFieldsDefinition,
_otdDirectives = [],
..
}
G.TypeDefinitionInterface G.InterfaceTypeDefinition {..} -> do
guard $ not $ null _itdFieldsDefinition
Just $
G.TypeDefinitionInterface
G.InterfaceTypeDefinition {_itdFieldsDefinition = sort _itdFieldsDefinition, ..}
G.TypeDefinitionUnion G.UnionTypeDefinition {..} -> do
guard $ not $ null _utdMemberTypes
Just $
G.TypeDefinitionUnion
G.UnionTypeDefinition {_utdMemberTypes = sort _utdMemberTypes, ..}
G.TypeDefinitionEnum G.EnumTypeDefinition {..} -> do
guard $ not $ null _etdValueDefinitions
Just $
G.TypeDefinitionEnum
G.EnumTypeDefinition {_etdValueDefinitions = sort _etdValueDefinitions, ..}
G.TypeDefinitionInputObject G.InputObjectTypeDefinition {..} -> do
guard $ not $ null _iotdValueDefinitions
Just $
G.TypeDefinitionInputObject
G.InputObjectTypeDefinition {_iotdValueDefinitions = sort _iotdValueDefinitions, ..}
genGraphQLTypeDefinitions :: Gen GraphQLTypeDefinitions
genGraphQLTypeDefinitions =
mkGraphQLTypeDefinitions <$> Gen.nonEmpty defaultRange (Gen.mapMaybe isValidTypeDefinition genTypeDefinition)
genRelationshipCapabilities :: MonadGen m => m RelationshipCapabilities
genRelationshipCapabilities = pure RelationshipCapabilities {}
@ -167,8 +110,7 @@ genCapabilities =
<*> Gen.maybe genQueryCapabilities
<*> Gen.maybe genMutationCapabilities
<*> Gen.maybe genSubscriptionCapabilities
<*> Gen.maybe genScalarTypesCapabilities
<*> Gen.maybe genGraphQLTypeDefinitions
<*> genScalarTypesCapabilities
<*> Gen.maybe genRelationshipCapabilities
<*> Gen.maybe genComparisonCapabilities
<*> Gen.maybe genMetricsCapabilities